JavaScript 原型以及原型链。

1. 原型

  • 隐式原型 __proto__

每个对象都有一个隐式原型__proto__,对象独有,null,undefined没有,并且在js中,Object.prototype__proto__null,因为是最顶端,如下代码:

var arr = [];
var obj = {};
var set = new Set();
var map = new Map();
var sym = Symbol();
var fn = function () {};
var date = new Date();
var reg = /\d+/;
var err = new Error();
var int = new Int8Array();
var float = new Float32Array();
var bool = true;

console.log(arr.__proto__, `arr.__proto__`);
console.log(obj.__proto__, `obj.__proto__`);
console.log(set.__proto__, `set.__proto__`);
console.log(map.__proto__, `map.__proto__`);
console.log(sym.__proto__, `sym.__proto__`);
console.log(fn.__proto__, `fn.__proto__`);
console.log(date.__proto__, `date.__proto__`);
console.log(reg.__proto__, `reg.__proto__`);
console.log(err.__proto__, `err.__proto__`);
console.log(int.__proto__, `int.__proto__`);
console.log(float.__proto__, `float.__proto__`);
console.log(bool.__proto__, `bool.__proto__`);

控制台输出:

image

当试图打印null,undefined__proto__

var nul = null;
console.log(nul.__proto__, `nul.__proto__`);

image

var unde = undefined;
console.log(unde.__proto__, `unde.__proto__`);

image

  • 显式原型prototype

prototype为函数独有,如下代码:

var arr = [];
var obj = {};
var set = new Set();
var map = new Map();
var sym = Symbol();
var fn = function () {};
var date = new Date();
var reg = /\d+/;
var err = new Error();
var int = new Int8Array();
var float = new Float32Array();
var bool = true;

console.log(arr.prototype, `arr.prototype`);
console.log(obj.prototype, `obj.prototype`);
console.log(set.prototype, `set.prototype`);
console.log(map.prototype, `map.prototype`);
console.log(sym.prototype, `sym.prototype`);
console.log(fn.prototype, `fn.prototype`);
console.log(date.prototype, `date.prototype`);
console.log(reg.prototype, `reg.prototype`);
console.log(err.prototype, `err.prototype`);
console.log(int.prototype, `int.prototype`);
console.log(float.prototype, `float.prototype`);
console.log(bool.prototype, `bool.prototype`);

控制台输出:
image
可以看出只有fn拥有prototype;

  • 隐式原型指向

隐式原型指向__proto__指向是根据它的构造函数的原型对象(prototype),比如var obj = {}; obj.__proto__指向的就是Object.prototype,只有为{}时,才是直接指向Object.prototype,例如var arr = []; arr.__proto__先指向的是Array.prototype,然后才是Object.prototype;如下代码:

var arr = [];
var obj = {};
var set = new Set();
var map = new Map();
var sym = Symbol();
var fn = function () {};
var date = new Date();
var reg = /\d+/;
var err = new Error();
var int = new Int8Array();
var float = new Float32Array();
var bool = true;

console.log(arr.__proto__ === Array.prototype, `arr.__proto__ === Array.prototype`);
console.log(obj.__proto__ === Object.prototype, `obj.__proto__ === Object.prototype`);
console.log(set.__proto__ === Set.prototype, `set.__proto__ === Set.prototype`);
console.log(map.__proto__ === Map.prototype, `map.__proto__ === Map.prototype`);
console.log(sym.__proto__ === Symbol.prototype, `sym.__proto__ === Symbol.prototype`);
console.log(fn.__proto__ === Function.prototype, `fn.__proto__ === Function.prototype`);
console.log(date.__proto__ === Date.prototype, `date.__proto__ === Date.prototype`);
console.log(reg.__proto__ === RegExp.prototype, `reg.__proto__ === RegExp.prototype`);
console.log(err.__proto__ === Error.prototype, `err.__proto__ === Error.prototype`);
console.log(int.__proto__ === Int8Array.prototype, `int.__proto__ === Int8Array.prototype`);
console.log(float.__proto__ === Float32Array.prototype, `float.__proto__ === Float32Array.prototype`);
console.log(bool.__proto__ === Boolean.prototype, `bool.__proto__ === Boolean.prototype`);

控制台输出

image

es5新增的class的指向。

class Person {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
    sayName() {
        console.log(this.name);
    }
}

const p = new Person();
console.log(p.__proto__ === Person.prototype, `p`);

image
通过构造函数new出来的实例的__proto__指向的它被创建出来的类的prototype

  • 原型指向解析

// 定义一个对象
var obj = {};
// obj 这个对象继承了 Object.prototype 上面的所有属性
// obj 自身没有名为 hasOwnProperty 的属性
// hasOwnProperty 是 Object.prototype 的属性
// 因此 obj 继承了 Object.prototype 的 hasOwnProperty
// Object.prototype 的原型为 null
// 原型链如下:
// obj ---> Object.prototype ---> null
obj.__proto__.a = 1;
console.log(obj.a, `obj.a`);
// 1
console.log(Object.prototype.a, `Object.prototype.a`);
// 1
console.log(obj.__proto__ === Object.prototype, `obj.constructor.a`);
// true

控制台输出

image

这时候打印 objconsole.log(obj);,如下图:

image

可以看到[[Prototype]]:Object,说明obj__proto__指向的是Object.prototype, 如果打印一个数组呢?var arr = []; console.log(arr),如下图:

image

可以看到[[Prototype]]:Array,说明arr__proto__指向的是Array.prototype,那么为什么说arr先是指向Array.protype,在指向Object.prototype呢,展开[[Prototype]]:Array后,如下图

image

可以看到最后指向的仍然是Object.prototype

  • 原型属性查找流程

首先定义一个类

class Person {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
    sayName() {
        console.log(this.name);
    }
}

通过new实例化

const p = new Person();

这时候p会有两个属性,name,age,通过访问p.name获取数据。如果访问p.foo则会返回undefined,这时候在p身上定义一个属性p.foo = 'foo',

p.foo = 'foo';
console.log(p.foo, `p`);
// foo

然后在访问p.foo,则返回foo;
从以上操作可以看出,在获取属性时,会首先从p身上查找属性foo,如果查找到则返回。然后删除p.foo属性,在Person类的prototype定义一个属性,Person.prototype.foo = 'foo';

const p = new Person('test', 15);
Person.prototype.foo = 'foo';
console.log(p.foo, `p`);
// foo

然后在访问p.foo,仍然可以返回foo;说明在查找属性时,如果自身p没有,则回去Person类的原型查找,查找到则返回,反之返回undefined
另外在访问属性时,js使用的方法是Object.getPrototypeOf,所以p.foo === Object.getPrototypeOf(p).foo,如果Object.getPrototypeOf(p).foo仍旧没有,它会继续查找 Object.getPrototypeOf(Object.getPrototypeOf(p)).foo

posted @ 2022-05-23 10:18  半糖也甜吖  阅读(39)  评论(0编辑  收藏  举报