new,create,class,extends,{}创建对象的原理及原型链查找规则

首发:https://zhuanlan.zhihu.com/p/91439580

原型,原型链属于js语言的基础,近期面试了很多同学,发现有很大一部分并不清楚原型,隐式原型,原型链之间的关系,加上自己在业务中分析源码遇到的一些问题,秉着知识回顾和总结的初心,详细总结下js原型相关的知识,有不足之处,还望大家补充。

几个概念:

原型:prototype

隐式原型:__proto__

本文将从创建对象的不同方法中引出原型链查找规则

创建对象的5个方法

在讲创建方法之前先来看下Object例子

const object_shadow = Object;
console.log(object_shadow)
console.log(typeof object_shadow) // function
console.log(typeof new object_shadow()) //object

调试上面的代码,可以得到如下结果:

object_shadow没有constructor,没有prototype

object_shadow的__proto__等于function (){}

=> function(){}的constructor等于 function Function(){}

=> function Function(){}的prototype 等于function() {}

可以得到:object_shadow._proto_ = object_shadow.constructor.prototype

说明:object_shadow在查找constructor没找到时,会通过__proto__向上查找,由于object_shadow并没有constructor,本质object_shadow会通过__proto__向上查找

另外Object是一个函数类型,所以可以使用new关键字

接下来看5中创建对象的方法

1,字面量创建对象

const test = {
  title:'字面量创建对象'
}

调试test:

从结果可以看出直接创建,_proto_指向Object

2,构造函数创建

function a(){
  this.title = "构造函数创建对象"
}

const A = new a()

执行过程:

创建一个字面量对象var obj = {}

设置新对象的constructor属性为构造函数的名称

指定obj的隐式原型等于a的prototype obj._proto_ = a.prototype

执行构造函数中的代码,构造函数中的this指向new出对象 a.call(obj)

将初始化完毕的新对象地址,保存到等号左边的变量中, 即赋值给A

调试A

可以看出:A._proto_ = A.constructor.prototype

3,Object.create()创建对象

const create_test = Object.create(test)
const create_a = Object.create(a)

create_test不满足 create_test._proto_ = create_test.constructor.prototype

create_a不满足 create_a._proto_ = create_a.constructor.prototype

console.log(create_a.title)//会得到undefined

Object.create(o)执行过程

    // 创造一个新匿名函数
    var F = function () {};
    // 给该匿名函数的原型指向o
    F.prototype = o;
    // 返回该匿名函数的实例
    return new F();

Object.create创建对象的过程包括了new

通过create创建的对象的_proto_指向构造函数F的prototype,通过上面的打印结果也可以看出。

4,class创建对象

class b {
  title(){
    return 'class创建对象'
  }
}

class创建的对象默认有prototype属性,不需要通过_proto_向上查找

5,extends创建对象

class extends_b extends b{}
//下面三个均报错
class extends_A extends A{}//TypeError: Class extends value #<Object> is not a constructor or null
class extends_test extends test{}/TypeError: Class extends value #<Object> is not a constructor or null
class extends_create_test extends create_test{}//TypeError: Class extends value #<Object> is not a constructor or null

extends创建对象 的 _proto_和prototype均指向被继承对象

由上面5种创建方法可以看出

1,class创建对象的方式,对象默认有prototype属性

2,Object.create的创建的对象没有prototype属性,只有_proto_属性指向传入的对象

3,new a()创建方式 _proto_属性指向constructor为a的对象,也没有prototype属性

4,字面量创建对象_proto_属性指向Object,也没有没有prototype属性

原型链查找规则

来看一个例子:

const test = {
  title:'字面量创建对象'
}
test.constructor.prototype.happy = 'constructor.prototype.happy'
test.constructor.prototype.pro_l = 'constructor.prototype.pro_l'
test.__proto__.pro_l = '_l'
console.log(test.pro_l)//'_l'

console.log(test.happy)//'constructor.prototype.pro_l'
create_a.constructor.prototype.__proto__.constructor.prototype.index = 3;
console.log(create_a.index)//3

test对象上没有happy,pro_l属性,默认向__proto_查找,此时如果_proto_上有属性,则获取,如果没有__proto_会指向_proto_的constructor的prototype,也就是会查找其构造函数的prototype,如果没有,继续查找当前_proto_的_proto_

再看一个例子

//A是上面的对象
A.__proto__.constructor.prototype.test_a = 'pro_test_a'
A.__proto__.__proto__.test_a = 'test_a'
console.log(A.test_a)//pro_test_a

如果A.__proto__.constructor.prototype.test_a不存在,则取A.__proto__.__proto__.test_a

//A是上面的对象
//A.__proto__.constructor.prototype.test_a = 'pro_test_a'
A.__proto__.__proto__.test_a = 'test_a'
console.log(A.test_a)//test_a 

constructor的prototype上没有属性时,继续查找当前__proto__的_proto_

 

如图:

总结:

1,对象的_proto_和A.constructor.prototype不一定相等,比如Object.create()就不是

2,{}创建对象效率是最高的,不需要执行构造函数,_proto_直接指向Object

3,new 一个对象的过程,新对象的_proto_指向该构造函数的prototype

4,Object.create()对象内部包含了new 运算符,新对象的_proto_指向传入的对象

5,new和Object.create()的区别,_proto_是否指向传入的对象

6,原型链查找

对象的prototype>_proto_>对象的constructor.prototype>_proto_._proto_

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值