原型链类问题

创建对象有几种方法

// 第一种方式:字面量
var o1 = {name:'o1'};
var o2 = new Object({name:'o2'});

// 第二种方式:通过构造函数
var M = function(name){this.name = name;}
var o3 = new M('o3');

// 第三种方式:Object.create
var p = {name:''p};
var o4 = Object.create(p);

原型、构造函数、实例、原型链

任何一个函数只要被new使用了,这个函数就可以叫构造函数

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

构造函数也是函数

函数都有一个prototype属性,这是在声明函数的时候JS自动加上的属性,这个prototype指的就是原型对象

这个prototype会初始化一个对象,也就是原型对象

 

原型对象如何区分被哪一个构造函数所引用呢?

原型对象中会有一个构造器constructor,这个构造器会默认你声明的函数

那么现在我们已经把原型、构造函数、实例三种之间的关联关系讲清楚了。

 

什么是原型链?

原型链就是说从一个实例对象往上找构造这个对象相关联的对象,那么这个对象往上找它又有创造它的原型对象,一直往上找,一直到Object.prototype终止,也就是说Object.prototype是整个原型链的顶端。

原理就是使用prototype和_proto_属性来完成向上查找

 

原型对象和原型链之间到底起什么样的作用?

构造函数中增加了很多属性和方法,我的实例可以共用这个,当我多个实例想去共用这个方法的时候,我不能每个实例都拷贝一份吧!它们之间可以考虑存在一个共同的原型对象上。

在访问一个实例的时候,这个实例有个方法,在这个实例本身它没有找到这个方法和属性,它往它的原型对象_proto_往上找,如果在它的原型对象上还没有找到,它会在它原型对象的基础上,在往上找,以此类推,直到Object.prototype,如果在Object.prototype还没有找到,原路返回,告诉他这个方法或这个属性没有定义,如果在中间任意一个环节找到了,它就停止向上查找,直接返回这个方法的用处。

 

强调一下,只有函数才有prototype,对象是没有prototype的。

只有实例对象有_proto_,函数也有这个_proto_,那是因为函数也是对象,所以它也会有这个属性。

 

instanceof的原理

看上图左侧有个实例对象,实例对象上有个_proto_属性,实力对象的_proto_属性引用的是它的构造函数的原型对象prototype,构造函数的属性引用原型对象,所以实例对象的属性是引用了原型对象。

instanceof属性的原理就是判断实例对象的这个_proto_和构造函数的prototype属性判断是不是同一个引用,instanceof在判断这个实例对象是不是这个构造函数的实例的时候,实际是在判断实例对象下面的属性_proto_和构造函数下的属性prototype是不是引用同一个地址。

但是原型有它的构造函数,那么用原型的对象的实例函数来判断是不是它的实例呢?这里返回也是ture,也就是说这条原型链上的构造函数,都是这个实例的构造函数,这样判断不严谨,但是instanceof都会返回true。

那么如果判断实例对象到底是哪个构造函数的实例用constructor判断更为准确

 

new运算符

大家都常用new运算符,但是new运算符背后的原理是什么?

答:

首先new运算符后面跟的是一个构造函数,new运算符的第一个步骤就是新的对象被创建,可以理解为一个字面量对象,就是一个空对象被创建了。

 

它是怎么被关联到实例对象上面呢?

然后这个新的对象是继承这个构造函数的原型对象的 foo.prototype的,这一点非常重要。

这时候原型链已经被关联一部分了,就是关于原型对象已经被关联上了,但是还没有关联到最终的实例对象上。

 

那我们还要再看第二步,构造函数被执行,这个非常必要;

如果构造函数不执行,那么构造函数体的所有和this相关的东西都是执行不了的,这个时候上下文this会指定新实例,也就是说我们刚创建的对象要与这个this要相关联,不然的话你没有办法把这个构造函数它的上下文转到这个新实例上的话,也就是刚才创建的新对象上,你最后返回的对象没法关联。

后面有个解释,这个也是官方的解释。

如果你没有参数传入的时候,这个new foo后面加不加括号都可以。

 

第三步:

构造函数如果返回一个对象,那么这个对象会取代new出来的结果。

这句话怎么解释,大家想一想:

我们第一步生成了一个新对象,
第二步执行上下文要把this指向我们新创建出来的对象,
第三步我们要不要返回,就是说new运算符不是要返回一个实例嘛,我们那个实例要不要返回我们的新建对象还是个问题,什么问题呢?

那要看你这个构造函数返回的了一个对象呢,还是没有返回,还是返回其他数值类型,这个是非常关键的步骤,如果说构造函数没有返回任何返回对象,也就是说只要你这个数据类型不是对象,那么new出来的结果就是步骤一传出来的对象,也就是我们刚才创建的新对象,如果它是有返回对象的,那么我们刚才那一步就前功尽弃了,直接返回构造函数的结果。

 

如果没有看明白,我写一串代码,来模拟这个new运算符

/**
 * 我们模拟这个先生成一个变量,我们用构造函数来写,它的参数是func,就是指定构造函数
 */

/**
 * 第一步:要先生成一个新对象
 * 新对象要指定Object的构造函数,比如说我们传入的func这个参数就是构造函数
 * 要指定构造函数原型对象,我们使用Object.create()的方式来创建
 * 也就是说我们新创建一个对象,这个对象要继承这个构造函数的原型对象
 */

/**
 * 第二步:要执行构造函数
 * 我们用k表示执行返回的结果,call就是用了转换上下文的,
 * 我们要把上下文转换成o对象,也就是我们刚刚创建的新的对象上
 */

/**
 * 第三步:要判断这个构造函数,执行完的结果是不是对象类型
 * 我们用typeof来判断,如果是对象类型就直接返回k
 * 如果不是就直接返回我们刚创建的对象 o
 * 这样就实现new运算符的效果
 */

var new2 = function(func){
    var o = Object.create(func.prototype);
    var k = func.call();
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值