1

ES5的原型链和ES6的类实现详解

JavaScript最初设计时受到了面相对象编程的影响,从而引入了new关键字,来实例化对象。而在ES5中new后面跟着的是构造函数(也是函数),而到了ES6则改成了clas了,而一开始new创建对象都是独立的对象,并不能像java那样拥有继承的概念,去共享变量和方法,为了解决这个问题,JavaScript就又给构造函数设计了一个prototype属性,这样所有私有的方法和变量就放到构造函数里面定义,而所有的公共的变量和方法就放到prototype对象里面,这样当构造函数创建一个实例化的对象的时候,就即拥有自己的私有变量和方法,也有公有的变量和方法了,实例化出来的对象的私有方法和变量修改都不会互相有影响,只有在修改公有的变量和方法的时候是对所有实例生效的。

ES5原型链

Example

function Person(name){
    this.name = name;
}
(function ($Person){
    $Person.prototype = {
        welcome: "hello",
        introduce: function(){
            return this.welcome + ",I am " + this.name;
        }
    }
})(Person)

var person1 = new Person("arvin");
var person2 = new Person("peter");
console.log(person1.introduce());   // hello,I am arvin
console.log(person2.introduce());   // hello,I am peter

person1.__proto__.welcome = "hi";
console.log(person1.introduce());   // hi,I am arvin
console.log(person2.introduce());   // hi,I am peter

代码解读:以上是本人推荐在使用ES5时,写原型链的方法,目的是为了代码简洁,方便复用,仅供参考。代码中在原型链上定义了一个welcome公共变量,这里要注意的是如果有同样名称的私有变量welcome时,原型方法introduce里面的this.welcome会首先查找私有变量welcome并使用,这个其实就和面相对象的覆写类似了。另外可以看出,ES5的构造函数(一般首字母大写以区分普通函数)在new的时候确实是创建了不同的区块来存放其私有变量name的值的,而对于原型链的变量welcome和方法introduce也确实是各个Person实例共用了同一块内存区域的,只要其中一个修改了原型链上的变量其他所有的对象实例再调用的时候从公共内存取出来的也就是被修改过只有的值了,这里要注意的是,构造函数new出来的实例对象,创建出来的指向原型链prototype的是其__proto__属性,也就是说person1.__proto__ === Person.prototype === person2.__proto__,这也从实际上证明了原型链对象在内存中只存了一份,是共用的

ES6类

Example

class Person {
    constructor(name) {
        this.name = name;
    }
    welcome = 'hello';  // S7才支持实例属性
    introduce(){
        return this.welcome + ",I am " + this.name;
    }
}

var person1 = new Person("arvin");
var person2 = new Person("peter");
console.log(person1.introduce());   // hello,I am arvin
console.log(person2.introduce());   // hello,I am peter

person1.__proto__.welcome = "hi";
console.log(person1.introduce());   // hi,I am arvin
console.log(person2.introduce());   // hi,I am peter

代码解读:上面暂时只是概念性的写法,事实上,ES6的类只是一个ES5原型链的语法糖而已,主要是从写法上更接近于面相对象的类而已,另外一个作用就是区分ES5的构造函数和函数之间的区分。

小结:对于ES5和ES6的类似面相对象和非面向对象的原因,以java为例提出以下几点个人见解:

1、java在继承(extend)的时候,子类是会复制一遍所有父类的方法和属性(除已覆写的除外)到一个独立的内存空间中的,即所有子类之间不存在任何的关系;而这点其实就和ES5的原型继承prototype和ES6的extend有很大的不同了。

2、java在new创建一个实例的时候同样是会开辟一个独立的属于该实例的内存空间,同一个类的实例之间互不影响;而ES5和ES6的创建实例的时候实例仍然是和类是存在关联的,而且是可以直接影响到类以及其他子类的公共状态和方法的。


夜里的太阳
469 声望16 粉丝

炫耀从来不是我的动机,好奇才是