深入浅出FE(五)Object.prototype

420 阅读4分钟

目录

1. 定义

2. 原理

3. 用法

4. 拓展

4.1 instanceof

4.2 ES6 class

5. 参考资料

1. 定义

Object.prototype 属性表示 Object 的原型对象。

2. 原理

准确的说:所有的原型对象都是Object构造函数创建的,Object.prototype除外;所有函数都是Function的实例,包括Function本身和Object。

我们创建的每个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。使用原型对象的好处是可以让所有对象实例共享它所包含的属性和方法。

function Person() {
}
Person.prototype.name = 'CoolSummer';
Person.prototype.age = '30';
Person.prototype.printName = function(){
    console.log(this.name)
}

var person1 = new Person();
person1.printName();//'CoolSummer'

var person2 = new Person2();
person2.printName();//'CoolSummer'

console.log(person1.printName === person2.printName);//true

看了很多的介绍原型的资料,我认为最好的是来自Nicholas.C.Zakas的《JavaScript高级程序设计》(第三版)中的图例

这张图完美解释了构造函数、原型和对象实例三者的关系,Person构造函数有一个prototype属性,这个属性指向构造函数的原型,构造函数的原型有一个constructor属性,指向构造函数,由这个构造函数派生出的对象实例Person1,Person2等内部有一个[[Prototype]]槽,这个槽指向构造函数的原型。另外这个[[Prototype]]槽在ECMAScript2015后可以通过Obeject.getProtypeOf()拿到这个值。不同的浏览器存在差异,在有些浏览器中可以通过对象实例._proto_属性拿到构造函数的原型对象。

MDN不再推荐通过对象实例._proto_属性拿到构造函数的原型对象。尽管某些浏览器可能仍支持它,但是它可能已经从相关的Web标准中删除,正在被删除或仅出于兼容性目的而保留。避免使用它,并尽可能更新现有代码;请参阅此页面底部的兼容性表以指导您做出决定。请注意,此功能可能随时停止起作用。

虽然Object.prototype.__proto__今天大多数浏览器都支持该功能,但它的存在和确切行为仅在ECMAScript 2015规范中被标准化为一项传统功能,以确保与Web浏览器的兼容性。为了获得更好的支持,建议Object.getPrototypeOf()改为使用。

//浏览器兼容性IE9+/FireFox3.5+/Safari5+/Openera12+/Chrome
console.log(Object.getPrototypeOf(person1) === Person.prototype);// true,

原型对象的优点是:所有的对象实例都可以共享它包含的属性和方法。这一点可以在构造函数里就可以看出来,因为构造函数在函数里面就定义了对象的实例信息,而原型对象可以在任何地方定义属性和方法。

原型对象的缺点有点像:“成也萧何,败也萧何”。原型对象最大的缺点就是它的共享本性造成的。这种共享对于原型构造内部的函数来说,没什么影响,但是对于属性来说问题就比较多了。如一个对象实例中设置了原型对象的值,那么其他的对象实例中的值也会改变。

3. 用法

1、使用构造函数(或者class)创建对象

由于原型的优点和缺点都是由于共享特性,所以我们使用原型创建对象时要尽量只在构造函数中定义方法,属性要从外部传入。

class Rectangle {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
  // Getter
  get area() {
    return this.calcArea();
  }
  // Method
  calcArea() {
    return this.height * this.width;
  }
}

const square = new Rectangle(10, 10);

console.log(square.area); // 100
上面的class当成是function即可,这其实也是一种组合使用构造函数和原型创建对象的模式。

2、动态原型模式创建对象

function Person(name, age){
    this.name = name;
    this.age = age;
    if(typeof this.printName != 'function'){
        Person.prototype.printName = function(){
            print(this.name);
        }      
    }
}

var person = new Person('CoolSummer', 30);
person.printName();//'CoolSummer'

var person2 = new Person('sky', 20);
person2.printName();//'sky'

4. 拓展

4.1 instanceof

instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。

function instanceof (left, right){
    let prototype = right.prototype;
    left = Object.getPrototypeOf(left);

    while(true){
        if(left === null){
            return false;
        }

        if(left === portotype){
            return true;
        }

        left = left.prototype;
    }
}

4.2 ES6 class

ECMAScript 2015(ES6)中引入的class主要是对JavaScript现有的基于原型的继承的语法糖。类语法不会向JavaScript引入新的面向对象的继承模型。

5. 参考资料

1. Object.prototype

2. MDN class

3. Nicholas.C.Zakas的《JavaScript高级程序设计》(第三版)---红宝书

4. MDN instanceof