JS: 原型链、__proto__、prototype

常常,我不懂原型,不懂__proto__和prototype,今天浅学一下。

prototype和__proto__都指向原型对象。

__proto__:对象属性。每个对象都有。

prototype:函数特有属性。

let obj = {};
console.log('__proto__' in obj); // true
console.log('prototype' in obj); // false

const fun = function () {};
console.log('__proto__' in fun); // true
console.log('prototype' in fun); // true

 并不是所有的 Function 对象都拥有 prototype 属性。典型的是箭头函数。

const fn = () => {};
console.log('prototype' in fn);             // false

const method = ({foo () {}}).foo;
console.log('prototype' in method);         // false

async function asyncFunction() {}
console.log('prototype' in asyncFunction);  // false

对象的__proto__属性,返回该对象的原型。

默认情况下,函数的prototype属性,指向一个对象。对于普通函数来说,该属性基本无用。对于构造函数来说,生成实例的时候,该属性会自动成为实例对象的原型。

let obj = {};
console.log(obj.__proto__ === Object.prototype); // true

const f = function () {}
console.log(f.__proto__ === Function.prototype); // true

const F = function () {};
console.log((new F()).__proto__ === F.prototype); // true

prototype 属性

作用:

JavaScript 继承机制的设计思想就是,原型对象的所有属性和方法,都能被实例对象共享。

构造函数的prototype属性,用来定义所有实例对象共享的属性和方法。

const Cat = function (name) {
  this.name = name;
}
Cat.prototype.host = '张三';
Cat.prototype.aowu = function () {
  return this.name + ': 嗷呜';
}
let nuomi = new Cat('糯米');
let tangyuan = new Cat('汤圆');
console.log(nuomi.aowu === tangyuan.aowu); // true
console.log(nuomi.host);                   // '张三'
console.log(tangyuan.host);                // '张三'

实例对象本身没有某个属性或方法的时候,它到原型对象去寻找该属性或方法。

实例对象自身    有某个属性或方法的时候,它不会再去原型对象寻找这个属性或方法。

扫描二维码关注公众号,回复: 14962001 查看本文章
tangyuan.host = '李四';
console.log(nuomi.host);     // '张三'
console.log(tangyuan.host);  // '李四'

constructor 属性

默认情况下,prototype对象有一个constructor属性,默认指向prototype对象所在的构造函数。

const Cat = function (name) {}
console.log(Cat.prototype.constructor === Cat); // true

let nuomi = new Cat('糯米');
console.log(nuomi.constructor === Cat);             // true
console.log(nuomi.hasOwnProperty('constructor'));   // false

nuomi是构造函数Cat的实例对象,但是nuomi自身没有constructor属性,该属性其实是读取原型链上面的Cat.prototype.constructor属性。

介绍完prototype,继续原型链相关。

原型链

上文说到,对于构造函数来说,生成实例的时候,prototype属性会自动成为实例对象的原型。

即:实例对象.__proto__ === 构造函数.prototype 。

首先,我们定义构造函数F,用它生成一个实例对象f。f.__proto__指向构造函数F的prototype属性。即:

function F() {}
const f = new F();
console.log(f.__proto__ === F.prototype); // true

构造函数F是Function的实例,所以F.__proto__指向Function的prototype属性。

console.log(F.__proto__ === Function.prototype); // true

默认情况下,函数的prototype属性,指向一个对象,即函数的prototype属性是Object的实例。

函数.prototype的 __proto__指向Object.prototype。
所以,F.prototype. __proto__ 和 Function.prototype. __proto__ 均指向Object.prototype。

console.log(F.prototype.__proto__ === Object.prototype);        // true
console.log(Function.prototype.__proto__ === Object.prototype); // true

但,除Function.prototype外,函数.prototype 的数据类型都是'object'

console.log(typeof Function.prototype); // 'function'
console.log(typeof Object.prototype);   // 'object'
console.log(typeof F.prototype);        // 'object'
console.log(typeof Date.prototype);     // 'object'

函数(包括构造函数,系统内置的函数对象(Function、Object等))都是Function的实例。所以Function.__proto__指向Function.prototype,就不会那么不容易理解。

console.log(Function.__proto__ === Function.prototype);  // true
console.log(Object.__proto__ === Function.prototype);    // true
console.log(Array.__proto__ === Function.prototype);     // true
console.log(Date.__proto__ === Function.prototype);      // true
console.log(RegExp.__proto__ === Function.prototype);    // true
console.log(Error.__proto__ === Function.prototype);     // true
console.log(Number.__proto__ === Function.prototype);    // true
console.log(String.__proto__ === Function.prototype);    // true
console.log(Boolean.__proto__ === Function.prototype);   // true

需要特别注意一下,Math,它不是构造函数。所有的属性和方法都在Math对象上调用。

console.log(Math.__proto__ === Object.prototype); // true

JavaScript 规定,所有对象都有自己的原型对象(prototype)。一方面,任何一个对象,都可以充当其他对象的原型;另一方面,由于原型对象也是对象,所以它也有自己的原型。因此,就会形成一个“原型链”(prototype chain):对象到原型,再到原型的原型……

JavaScript中,万物皆对象!如果一层层地上溯,所有对象的原型最终都可以上溯到Object.prototype。即所有对象都继承了Object.prototype的属性。

Object.prototype的原型呢?是null。null没有任何属性和方法,也没有自己的原型。因此,原型链的尽头就是null。

console.log(Object.prototype.__proto__ === null); // true

另:__proto__并非标准属性(ECMA-262第5版将该属性或指针称为[[Prototype]]),只有浏览器才需要部署,其他环境可以没有这个属性。

因此,应尽量少用这个属性,而是用Object.getPrototypeOf()和Object.setPrototypeOf(),进行原型对象的读写操作。

至此,原型链相关知识基本梳理完毕。综上,可以绘制原型链的图谱了。

最后:

instanceof 运算符:运算符返回一个布尔值,表示对象是否为某个构造函数的实例。运算符左边是实例对象,右边是构造函数。原理是检查右边构造函数的prototype属性,是否在左边对象的原型链上

Object.prototype.isPrototypeOf():用来判断该对象是否为参数对象的原型。

function F() {}
const f = new F();
console.log(f instanceof F);                    // true
console.log(f instanceof Object);               // true
console.log(F.prototype.isPrototypeOf(f));      // true
console.log(Object.prototype.isPrototypeOf(f)); // true

// 一种特殊情况,判断失真
let obj = Object.create(null);
console.log(typeof obj);                             // 'object'
console.log(obj instanceof Object);                  // false
console.log(Object.prototype.isPrototypeOf(obj));    // false

The end.

猜你喜欢

转载自blog.csdn.net/weixin_43932309/article/details/130045017