新特性:
设置原型:
前一篇有一点没有说的是:__proto__并不是ECMAScript标准,尽管有很多浏览器实现了这个属性。
ES5的时候引入了Object.create():
var a = {a: 1}; // a ---> Object.prototype ---> null
var b = Object.create(a); // b ---> a ---> Object.prototype ---> null
这个在上一篇见过,但是理解不够透彻,其实就是ES提供的设置原型的方法。
ES标准在ES6的时候又引入了两个方法来做这件事儿。
Object.getPrototypeOf(someObject) === someObject.__proto__
//另一个就是设置原型
son.__proto__ = Parent.prototype; //非标准
Object.setPrototypeOf(son, Parent.prototype);
讲真的,这个标准的写法比__proto__弱多了,简直是强行增加思维难度。
class语法糖:class constructor extends super static
class Person{
constructor(name){
this.name = name;
}
run(){
console.log(this.name + ' running');
}
}
class Man extends Person{
constructor(name, sex){
super(name);
this.sex = sex;
}
mySex(){
console.log(this.name + ' is ' + this.sex);
}
static Version(){ //用于静态方法
console.log('v1.0');
}
}
var m1 = new Man('lisi','male');
var m2 = new Man('wangwu', 'female');
原型链:续
昨天看了知道了原型链概念,但是对于__proto__和prototype还是模模糊糊的。分析了一番之后得出结论:
- __proto__(对象都有的) ,指向当前对象的原型对象,类比到class机制下,就是指向当前类的父类
- prototype(function都有的对象),创建函数的同时,会生成一个原型对象,类比到构造方法和类的关系
有了上面的两条结论,再去理解原型链就很容易了。关键是和一般的面向对象做对比来。
function Person(props){
this.name = props.name || 'noname';
}
Person.prototype.run = function(){ //相当于类方法
console.log(this.name + ' running');
}
function createPerson(props){
return new Person(props || {});
}
var p = createPerson();
以昨晚的代码为例,我们很容易写出原型链:
p.__proto__ === Person.prototype //p继承Person构造方法对应的原型也就是Person.prototype
Person.prototype.__proto__ === Object.prototype //同理
Object.prototype.__proto__ === null //原型链终点
理解的难点在于要清楚,JS不用创建类,也没法创建类。使用原型的概念去替代类。然后这个原型也不需要手动去创建,当你写一个方法的时候,这个方法就会具有一个prototype对象,这个对象就是原型。
Person.prototype.constructor === Person //原型的构造函数 自动绑定到Person
到这里原型链基本就没什么秘密了。接下来看在这种模式下,继承如何实现呢。
继承:
1. 继承第一点,肯定是要获取父类的属性和方法,做法如下:
function Man(props){
Person.call(this, props); //这一步相当于复用了Person的代码
this.sex = props.sex || 'male';
}
就是把this绑定到父类用apply或call调用即可。
2. 把原型链设置正确:
Man.prototype.__proto__ = Person.prototype //非标准
Man.prototype = Object.create(Person.prototype); //ES5标准
Object.setPrototypeOf(Man.prototype, Person.prototype); //ES6标准
当然,在ES6后又提出了一套class语法糖,原理还是一样的原理,不过有了语法糖写起来就很符合习惯。
继承还有一种方法,利用一个架桥函数,不过说实话这种方法,在ES5 ES6之后出来之后,意义不大。做法如下:
function F(){} //空函数
F.prototype = Person.prototype; //把这个空函数原型对象设为父类
Man.prototype = new F(); //子类的原型对象设为F的实例
Man.prototype.constructor = Man; //修改子类的构造函数