有关 Object Prototypes 对象原型你不得不知道的

若是进入到Javascript 面向对象的领域,那么对象原型Object Prototypes你就必定会接触到,我也是之前看的云里雾里的,总感受空洞,和实际使用的场景结合不起来,但最近看了一篇文章,感受清楚多了,因此分享给你们,但愿有所帮助吧。javascript

1 什么是对象原型?

对象都有一个原型属性,经过__proto__(称为dunder proto)得到,这个属性强烈不建议直接经过dot符读取或者修改, MDN 里面有特别强调,这个原型属性是一个指针,指向另外一个对象java

var obj = {};
console.log(obj.__proto__);
// -> {constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}

 

对象原型有三点要注意:数组

1 每一个对象都有一个__proto__属性安全

2 对象字面量的__proto__恒等于Object.prototype函数

3 Object.prototype的__proto__ 等于null性能

 

2 __proto__有什么用?

用于对象属性的查找,Javascript engine查找对象属性的时候,首先查找对象自己,若是没有,会查找对象原型所指向的对象,若是仍然没有找到,会查找原型的原型,直到某个对象的__proto__为null,这就是原型链this

 

var obj = {};
obj.__proto__.testValue = 'Hello!';  // 注意,这里的操做是为了演示,不推荐操做__proto__

console.log(obj); // -> {}
console.log(obj.testValue); // -> Hello!

 

3 有关的方法和属性

obj.hasOwnProperty()

判断某个属性是不是obj本身的spa

var obj = {};
obj.__proto__.testValue = 'Hello!'; // 注意,这里的操做是为了演示,不推荐操做__proto__
 
console.log(obj.hasOwnProperty('testValue'));
// -> false

console.log(obj.hasOwnProperty('__proto__'));
// -> false

console.log(obj.__proto__.hasOwnProperty('testValue'));
// -> true

 

Object.getOwnPropertyNames()

得到对象本身的属性名组成的数组prototype

var obj = { prop: 'Hi there!' };
obj.__proto__.testProp = 'Hello!';
console.log(Object.getOwnPropertyNames(obj)); // -> [ 'prop' ]

 

Object.getPrototypeOf()

得到对象的原型指针

var obj = {};
console.log(Object.getPrototypeOf(obj) === obj.__proto__);
// -> true

 

Object.setPrototypeOf()

设置原型对象

var obj = {};
var protoObj = {};
Object.setPrototypeOf(obj, protoObj);
console.log(Object.getPrototypeOf(obj) === protoObj);
// -> true

 

 

4 原型继承

Javascript中的继承是经过原型完成的...

4.1 Function Prototypes & new

函数的prototype不一样于它的__proto__属性,看起来有点绕

functions's prototype's __proto__ === Object.prototype

function fn() {}
var protoOfPrototype = Object.getPrototypeOf(fn.prototype);

// protoOfPrototype === fn.prototype.__proto__
console.log(protoOfPrototype === Object.prototype);
// -> true

 

但咱们使用new 调用function 时, Javascript engine会设置this的__proto__ 等于function的prototype,这就是继承的关键

function PersonConstructor(name, age) {
    // this = {};
    // this.__proto__ = PersonConstructor.prototype;

    // Set up logic such that: if
    // there is a return statement
    // in the function body that
    // returns anything EXCEPT an
    // object, array, or function:
    //     return 'this' (the newly
    //     constructed object)
    //     instead of that item at
    //     the return statement;

    this.name = name;
    this.age = age;

    // return this;
}

 

到这里,能够得出三点:

1 使用new 建立的函数对象的__proto__等于该函数的prototype

2 function's prototype's__proto__ 等于Object.prototype

3 Object.prototype's__proto__等于null

function Fn() {}
var obj = new Fn();

var firstProto = Object.getPrototypeOf(obj);
// firstProto === obj.__proto__
console.log(firstProto === Fn.prototype); // -> true

var secondProto = Object.getPrototypeOf(firstProto);
// secondProto === obj.__proto__.__proto__
console.log(secondProto === Object.prototype); // -> true

var thirdProto = Object.getPrototypeOf(secondProto);
// thirdProto === obj.__proto__.__proto__.__proto__
console.log(thirdProto === null); // -> true

 

注意:区别于1中的对象字面量

 

4.2 实现继承

咱们能够安全地操做function的prototype,好比修改function's prototype的方法或属性,那么经过new 建立的对象就会继承这些方法或属性

function Fn() {}

Fn.prototype.print = function() {
    console.log("Calling Fn.prototype's print method");
};


var obj = new Fn();
obj.print(); // -> Calling Fn.prototype's print method

 

若是咱们把print方法写在Fn的构造函数里呢?效果是否是同样?

function Fn() {
    this.print = function() {
        console.log("Calling Fn.prototype's print method");
    };
}

var obj = new Fn();
obj.print(); // -> Calling Fn.prototype's print method

 

不一样点是:若是print写在构造函数里,那么经过new生成的每一个对象都有一份print方法,在内存和性能上和写在原型上是不同的,写在原型是是全部new出来的对象共用一份print方法

 

下面是一份对比试验,能够看出建立print方法在prototype上的对象比建立print方法在构造函数中的对象快一倍左右

function FuncOnThis () {
  this.print = function () {
    console.log('Calling print')
  }
}

let arr = []
console.time('FuncOnThis')
for (let i = 0; i < 2000000; i++) {
  arr.push(new FuncOnThis())
}

console.timeEnd('FuncOnThis')


// FuncOnThis: 763.890ms

function FuncOnProto () {
}

FuncOnProto.prototype.print = function () {
  console.log('Calling print')
}

console.time('FuncOnProto')

let arr2 = []
for (let i = 0; i < 2000000; i++) {
  arr2.push(new FuncOnProto())
}

console.timeEnd('FuncOnProto')

// FuncOnProto: 334.594ms

 

4.3 字面量的__proto__

如今已经知道object's __proto__等于建立该对象的function's prototype, object 字面量来自Object, array 来自 Array, function来自Function

console.log(
    Object.getPrototypeOf({}) === Object.prototype
); // -> true

console.log(
    Object.getPrototypeOf([]) === Array.prototype
); // -> true

console.log(
    Object.getPrototypeOf(function fn() {})
    === Function.prototype
); // -> true

 

4.4 构造函数constructor

function's prototype都有一个constructor属性,这是一个指针,指向函数自己

function Fn() {}
console.log(Fn.prototype.constructor === Fn);
// -> true

因此,使用new建立的对象,它的构造函数也指向该函数,沿着原型链向上查找,会找到Fn's prototype’s constructor

function Fn(){}
var obj = new Fn();

console.log(obj.constructor);
// -> [Function: Fn]

 

经过对象的构造函数属性,能够知道该对象是哪一个函数建立的

function Fn() {};

var normalObj = {};
var fnObj = new Fn();

console.log(normalObj.constructor); // -> [Function: Object]
console.log(fnObj.constructor); // -> [Function: Fn]

 

4.5 自定义原型建立对象

Object.create(param)

这个函数用于建立对象,接收一个对象做为参数,建立的新对象的__proto__等于传入的参数

var prototypeObj = {
    testValue: 'Hello!'
};

var obj = Object.create(prototypeObj);
console.log(obj); // -> {}

console.log(
    Object.getPrototypeOf(obj) === prototypeObj
); // -> true

console.log(obj.testValue); // -> 'Hello!'

Object.create()提供了一种更灵活的方式去扩展原型链,使得对象不只继承于function's prototype, 而是任何object

 

 

总结:

1 function's prototype 不一样于 __proto__

2 functions' prototype's __proto__ 等于Object.prototype

3 Object.prototype's __proto__等于null

4 使用new建立函数对象的__proto__等于该构造函数的prototype

 

 

参考资料:

https://www.educative.io/collection/page/5679346740101120/5707702298738688/5665117697998848

 

https://www.educative.io/collection/page/5679346740101120/5707702298738688/6330230964748288

相关文章
相关标签/搜索