prototype原型链详解

原型链

  • 创建 (声明) 对象有几种方法
  • 原型、构造函数、实例、原型链
  • instanceof 的原理
  • new 运算符

一. 创建对象有几种方法

1.字面量

var test2 = {x:123,y:345};
console.log(test2);//{x:123,y:345};
console.log(test2.x);//123
console.log(test2.__proto__.x);//undefined
console.log(test2.__proto__.x === test2.x);//false

2.构造函数new

// 方法1 #通过new Object声明的一个对象
var test1 = new Object({x:123,y:345});
console.log(test1);//{x:123,y:345}
console.log(test1.x);//123
console.log(test1.__proto__.x);//undefined
console.log(test1.__proto__.x === test1.x);//false

// 方法2  #使用显式构造函数创建对象
var M = function(name){ this.name = name;};
var o3 = new M('o3');  //M {name: "o3"}

new的作用: 1.创了一个新对象; 2.this指向构造函数; 3.构造函数有返回,会替换new出来的对象,如果没有就是new出来的对象

3.内置方法

Obejct.create(obj,descriptor),obj是对象,describe描述符属性(可选),创建一个具有指定原型且可选择性地包含指定属性的对象。

let p = {x:123,y:345};
let test = Object.create(p);
console.log(test);//{}
console.log(test.x);//123
console.log(test.__proto__.x);//3
console.log(test.__proto__.x === test.x);//true
console.log(test.__proto__ === p)  // true

Object.create方法是把参数中这个对象作为一个新对象的原型对象赋给test的,test本身不具备这个属性的,通过原型链来连接这个原型对象的。(所以test对象本身没有name这个属性,只能通过原型链来找name属性。

三种方法的优缺点

  1. 功能:都能实现对象的声明,并能够赋值和取值
  2. 继承性:内置方法创建的对象继承到__proto__属性上
  3. 隐藏属性:三种声明方法会默认为内部的每个成员(属性或方法)生成一些隐藏属性,这些隐藏属性是可以读取和可配置的,属性分类见下面:
  4. 属性读取:Object.getOwnPropertyDescriptor()或getOwnPropertyDescriptors()
  5. 属性设置:Object.definePropertype或Object.defineProperties

二. 原型链的关系

下图仔细观察原型、构造函数、对象实例、原型链之间的关系。

原型链的关系

1.对象实例

​ 只要是对象就是一个实例;回顾上面的创建对象的几种方式,任何一个实例对象都有一个隐式原型__proto__对象。

2.构造函数

凡是通过关键字new来操作后边的函数,这个函数就是构造函数;准确的说任何一个函数只要被new使用了,后面这个函数就可以被叫做构造函数。

构造函数可以使用new运算符来生成一个实例;

3.原型对象

任何一个函数都有一个prototype属性,他是函数所独有的,这个prototype指的就是显式原型对象;

任何一个实例对象都有一个__proto__对象。他是对象所独有的,这个__proto__指的就是隐式原型对象;

__proto__是原型链查询中实际用到的,它总是指向 prototype

prototype 是函数所独有的,在定义构造函数时自动创建,它总是被__proto__所指。

4.原型链

每个对象都可以有一个原型_proto_,这个原型还可以有它自己的原型,以此类推,形成一个原型链。查找特定属性的时候,我们先去这个对象里去找,如果没有的话就去它的原型对象里面去,如果还是没有的话再去向原型对象的原型对象里去寻找知道终点null...... 这个操作被委托在整个原型链上,这个就是我们说的原型链了

原型链是通过什么来实现这个往上找的过程呢?
通过prototype这个原型和__proto__属性来完成原型链的查找的;

img

let obj1 = {name:'lucas'};   
obj1.__proto__ === Object.prototype  // true
obj1.__proto__.__proto__            // null  #这是原型链顶端了
Object.prototype.__proto__          // null #这是原型链顶端了

function Person(){}
Person.prototype.__proto__.__proto__  // null #这是原型链顶端了

let person = new Person();
person.__proto__.__proto__.__proto__  // null #这是原型链顶端了
person.__proto__  === Person.prototype  // true
function M (name) { 
    this.name = name; 
}//person是构造函数

var o3 = new M('o3') // personTwo是实例

原型关系详解

原型对象都有一个默认的constructor属性指向构造函数

三. instanceof 原理

instanceof主要用于判断某个实例是否属于某个类型,也可用于判断某个实例是否是其父类型或者祖先类型的实例。

instanceof主要的实现原理就是只要右边变量的 prototype 在左边变量的原型链上即可。因此,instanceof在查找的过程中会遍历左边变量的原型链,直到找到右边变量的 prototype,如果查找失败,则会返回 false。

实例对象上有__proto__这个属性,实例对象的这个属性引用是它构造函数的原型对象(也就是找到的这个构造函数);
构造函数有prototype这个属性,这个属性引用的原型对象,在往下走,实例对象的__proto__这个属性,其实是引用这个原型对象。

instanceof原理图

模拟开发instanceof

function instanceof(left, right) {
    const rightVal = right.prototype
    const leftVal = left.__proto__
    // 若找不到就到一直循环到父类型或祖类型
    while(true) {
        if (leftVal === null) {
            return false
        }
        if (leftVal === rightVal) {
            return true
        }
        leftVal = leftVal.__proto__ // 获取祖类型的__proto__
    }
}

四. new 运算符

new运算符

var new2 = function(func){

    //1.创建一个空对象,这个对象要继承这个构造函数的原型对象(空对象要关联构造函数的原型对象;)
    let o = Object.create(func.prototype);
    
    //2.执行构造函数
    let k = func.call(o);//call用来转移上下文(this),把这个上下文转成o对象
    
    //3.判断构造函数的运行结果是不是对象类型
    if(typeof k ==='object'){
        return k;
    }else{
        return o;
    }
 };

猜你喜欢

转载自www.cnblogs.com/jing-tian/p/12244374.html