JS里的面向对象

面向对象,即 OO(Object Oriented)

理解面向对象

面向对象有如下的概念

  • Class 类
    定义对象的特征,它是对象的属性和方法的模板定义
  • Object 对象
    类的一个实例
  • Property 属性
    对象的特征,比如颜色
  • Method 方法
    对象的能力,比如行走
  • Constructor 构造函数
    函数初始化瞬间被调用,通常它的名字与包含它的类名相同
  • Inheritance 继承
    一个类可以继承另一个类的特征
  • Encapsulation 封装
    一种把数据和方法绑定在一起使用的方式
  • Abstraction 抽象
    结合复杂的继承、方法、属性的对象能够模拟现实的模型
  • Polymophsim 多态
    ‘许多形态’, 表示不同的类可以定义相同的方法和属性

命名空间

如下代码

var MYAPP = MYAPP || {};

这段代码是什么意思,为何一个变量的定义要用它自身来与上一个对象?这里就牵扯到 JS 的 5 个 ‘falsy’ 值,即

  • NaN、0
  • ‘’(空字符串)
  • false
  • null
  • undefined

在 JS 中,&&|| 符号是这样使用的

  • 对于 && 来说,它会从左往右一直寻找一个 ‘falsy’ 值,找到之后直接返回这个 ‘falsy’ 值,剩下的就不管它

    1 && 0 // 0
    1 && 0 && 2 && 3 // 0
    1 && console.log(3) // undefined, 因为 console.log() 的返回值就是 undefined
  • 对于 || 来说,它会从左往右一直寻找一个非 ‘falsy’ 值,找到之后直接返回这个 ‘falsy’ 值,剩下的就不管它

    0 || 1 // 1
    1 || 0 || 2 || 3 // 1
    0 || undefined || null || 1 // 1

这其实很好理解,对于 &&,只要其中条件有一个不成立(falsy)就全不成立; 而对于 ||,只要其中条件有一个成立(trusy)就全成立。于是浏览器就根据符号找到相应的值就返回。

上面说到的代码就相当于

if (MYAPP) {
; // 不做
} else {
MYAPP = {}; // 等于一个对象
}

再来谈下 this

有如下代码

button.onclick = function() {
console.log(this);
}
button.addEventListener('click', function() {
console.log(this);
});
// jQuery
$('ul').on('click', 'li', function() {
console.log(this);
})

代码中的 this 是什么,前两个中的 this 在 MDN 的文档中表示是 “触发事件的元素”,亦即 button,后一个在 jQuery 的文档中表示是 “与 Selector 相匹配的元素” ,亦即 li

但是,有一种特殊情况如下

button.onclick = function() {
console.log(this);
}
button.onclick.call({name: 'frank'});

那么这个时候 this 是什么,就是 call 后面传的第一个参数,亦即 {name: 'frank'} 这个对象

还有一个例子

function X() {
return object = {
name: 'object',
options: null,
f1(x) {
this.options = x;
this.f2();
},
f2() {
this.options.f2.call(this); // 这里的 this 都是指向 object
}
}
}

var options = {
name: 'options',
f1() {},
f2() {
console.log(this);
}
};

var x = X();
x.f1(options);

以上最终代码打印的是 object 这个对象

new 这个关键字

假设有一个 person 对象,它的实现是这样

function person(options) {
this.name = options.name;
this.age = options.age;
}
person.prototype = {
getAge: function() {
return this.age;
},
}

我们知道,我们在 new 一个对象的时候,时常会这么做

let frank = new person({
name: 'frank',
age: 18
});

然后调用它的函数

frank.getAge(); // 18

但是你有没有想过,如果我不用 new 和 this 呢? 我该怎么做?
不用 new 以及 this 的情况下,你可以这样写代码

function person(options) {
let temp = {};
temp.name = options.name;
temp.age = options.age;

// 这句话很重要,如果不指向就调用不到 getAge
temp.__proto__ = person.prototype;

return temp;
}
person.prototype = {
getAge: function() {
return this.age;
},
}

然后你才可以这样使用

let frank = person({
name: 'frank',
age: 18
});
frank.getAge(); // 18

由以上我们可以看到,我们是不是省略了一些代码?我们省略的是哪些代码呢?
用了 new 之后

  1. 我们不用分配一个临时对象了, JS 帮我们创建了这个临时对象
  2. 我们不用 return 这个临时对象了,JS 帮我们 return
  3. 我们不用指向自己定义的对象的原型(prototype)了, JS 帮我们自己指向
  4. 因为指向原型的关系,所以我们用 this 就可以访问到这个临时变量

换言之, 我们终于可以不用那么费劲心思的去定义一个对象,new 都帮我们弄好了

以下是使用 new 的时候的一些注意点

var object = new Object()

// 自有属性 空
object.__proto__ === Object.prototype //false

var array = new Array('a','b','c')

// 自有属性 0:'a' 1:'b' 2:'c',length:3
array.__proto__ === Array.prototype
Array.prototype.__proto__ === Object.prototype

var fn = new Function('x', 'y', 'return x+y')
// 自有属性 length:2, 不可见的函数体: 'return x+y'
fn.__proto__ === Function.prototype

// Array is a function
Array = function(){...}
Array.__proto__ === Function.prototype

// Object is a function
Object = function(){...}
Object.__proto__ === Function.prototype

参考链接

JS new 到底是干嘛的