深入理解new操作符

深入理解new操作符new的定义:1、创建一个新的对象;2、将构造函数的prototype赋值给新对象的_proto_;3、构造函数中的this指向新对象,并且调用构造函数;4、如果构造函数无返回值,或者不是引用类型,返回新对象;否则为构造函数的返回值。instanceofinstanceof运算符用于判断

大家好,欢迎来到IT知识分享网。

new的定义:

1、创建一个新的对象;

2、将构造函数的prototype赋值给新对象的_proto_;

3、构造函数中的this指向新对象,并且调用构造函数;

4、如果构造函数无返回值,或者不是引用类型,返回新对象;否则为构造函数的返回值。

instanceof

instanceof运算符用于判断一个对象的原型链是否存在一个构造函数的prototype属性。

语法:object instanceof constructor 

参数:object(要检测的对象)  constructor(某个构造函数)

描述:instanceof运算符用来检测constructor。prototype是否存在于参数object的原型链上

下面通过代码阐述instanceof的内部机制,假设有x instanceof y一条语句,则其内部实际做了如下判断:

while(x.__proto__!==null) {
    if(x.__proto__===y.prototype) {
        return true;
        break;
    }
    x.__proto__ = x.__proto__.proto__;
}
if(x.__proto__==null) {return false;}

 

x会一直沿着隐式原型链__proto__向上查找直到x.__proto__.__proto__...===y.prototype为止,如果找到则返回true,即xy的实例,否则返回falsex不是y的实例。

相关面试题

function F() {}
function O() {}

O.prototype = new F();
var obj = new O();

console.log(obj instanceof O); // true
console.log(obj instanceof F); // true
console.log(obj.__proto__ === O.prototype); // true
console.log(obj.__proto__.__proto__ === F.prototype); // true

 

根据new的内部机制改写代码

function F() {}
function O() {}

var obj = (function () {
    var obj1 = {};
    obj1.__proto__ = F.prototype; // new F();
    O.prototype = obj1; // O.prototype = new F();
    obj.__proto__ = O.prototype; // new O();
    obj.__proto__ = obj1;
    return obj;
})()

 

如果改一下代码顺序,结果将不同

function F() {}
function O() {}

var obj = new O();
O.prototype = new F();

console.log(obj instanceof O); // false
console.log(obj instanceof F); // false
console.log(obj.__proto__ === O.prototype); // false
console.log(obj.__proto__.__proto__ === F.prototype); // false

 

和其他高级语言一样javascript也有new操作符,我们知道new可以用来实例化一个类,从而在内存中分配一个实例对象。但在JavaScript中,万物皆对象,那么为什么还要通过new来产生对象?接下来就来了解一下new的特性:

function Animal(name) {
  this.name = name;
}
Animal.color = "black";
Animal.prototype.say = function() {
  console.log("I'm" + this.name);
};
var cat = new Animal("cat");
console.log(
  cat.name,  //cat
  cat.height  //undefined
);
cat.say();//I'm cat
console.log(
  Animal.name,//Animal
  Animal.color//back
);
Animal.say();//Animal.say is a function

代码解读:

1-3:创建了一个叫Animal的函数,并在其this上定义了属性:name,name的值是函数被执行时的形参。

4:在Animal对象(Animal本身是一个函数对象)上定义了一个静态属性:color,并复制“black”

5-7:在Animal函数的原型对象prototype上定义了一个say()方法,say方法输出了this的name值。

8:通过new关键字创建了一个新对象cat

9-1:3:cat对象尝试访问name和color属性,并调用say方法。

14-18:Animal对象尝试访问name和color属性,并调用say方法

剖析new的内部原理:

var cat = new Animal(“cat”);

Animal本身是一个普通函数,但当通过new来创建对象时,Animal就是构造函数。

JS引擎执行这句代码时,在内部做了很多工作,用伪代码模拟其内部流程如下:

new Animal('cat') = {
  var obj = {};
  obj._proto_ = Animal.prototype;
  var result  = Animal.call(obj,"cat");
  return typyof result === 'object' ? result : obj;
}

将上述流程分为4个步骤来理解:

1、创建一个空对象obj;

2、把obj_prototype_指向构造函数Animal的原型对象prototype,此时便建立了obj对象的原型链:obj->Animal.prototype->Object.prototype->null

3、在obj对象的执行环境调用Animal函数并传递参数“cat”.相当于var result = obj.Animal(“cat”).当这句执行完之后,obj便产生了属性name并赋值“cat”。关于 call 的用法请参考:深入理解 call、apply 和 bind

4、考察第3步的返回值,如果无返回值或者返回一个非对象值,则将obj作为新对象返回:否则会将result作为新对象返回。根据以上过程,我们发现cat其实就是第四步的返回值,因此我们对cat对象的认知就多一点:

  • cat的原型链是:cat->Animal.prototype->Object.prototype->null
  • cat上新增了一个属性:name

分析完了cat的生产过程,在分析一下输出结果:

  • cat.name – 在第三步中,obj对象就产生了name属性。因此cat.name就是这里的obj.name
  • cat.color – cat 对象先查找自身的color,没有找到便会沿着原型链查找,在上述例子中,我们仅对Animal对象定义了color,并没有在其原型链上定义,因此找不到。
  • cat.say – cat会先查找自身的say方法,没有找到便会沿着原型链找,在上述例子中,我们在Animal的prototype上定义了say,因此在原型链找到了say方法。

另外,在say方法中还访问this.name,这里的this指的是其调用者obj,因此输出的是obj.name的值。

对于Animal来说,它本身也是一个对象,因此它在访问属性和方法时也遵守上述查找规则,所以:

  • Animal.color >  ” black “
  • Animal.name >  ” Animal ” 
  • Animal.say() >   Animal.say is not a function

需要注意的是:Animal先查找自身的name,找到了name,但这个name并不是我们定义的name,而是函数对象的属性,一般情况下,函数对象在生产时会内置name属性并将函数名作为赋值(仅函数对象)。

另外,Animal 在自身没有找到 say() 方法,也会沿着其原型链查找,Animal 的原型链如下所示:

 

深入理解new操作符

 Animal 的原型链: Animal->Function.prototype->Object.prototype->null

 由于 Animal 的原型链上也没有定义 say 方法,因此返回异常提示。

探索new意义:

1、cat继承了Animal对象

2、cat是Animal的实例

cat 是通过 new 产生的对象,那么 cat 到底是不是 Animal 的实例对象? 我们先来了解一下JS是如何来定义实例对象

1
A instanceof B

如果上述表达式为 true,JavaScript 认为 A 是 B 的实例对象,我们用这个方法来判断一下 cat 和 Animal 。

1
cat instanceof Animal;
//true

从结果看,cat 确实是 Animal 实例,要想更加证实这个结果,我们再来了解一下 instanceof 的内部原理:

1
2
3
4
var 
L = A.__proto__;
var 
R = B.prototype;
if
(L === R)
    
return 
true
;

如果 A 的__proto__ 等价于 B 的 prototype,就返回 true 。

在 new 的执行过程【2】中,cat 的 __proto__ 指向了Animal 的 prototype,所以 cat 和 Animal 符合 instanceof 的判断结果。

因此,通过 new 创建的 对象 和 构造函数 之间建立了一条原型链,原型链的建立,让原本孤立的对象有了依赖关系和继承能力,让JavaScript 对象能以更合适的方式来映射真实世界里的对象,这是面向对象的本质。

下面是一个经典例子,涉及 newthis、以及原型链相关问题,请看代码:
function Foo(){
    getName = function(){
        console.log(1)
    }
    return this;
}
Foo.getName = function(){
    console.log(2)
}
Foo.prototype.getName = function(){
    console.log(3)
}
var getName = function(){
    console.log(4)
}
function getName(){
    console.log(5)
}
// ouput:
Foo.getName();
getName();
Foo().getName();
getName();
new Foo.getName();
new Foo().getName();
new new Foo().getName();

 

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/34544.html

(0)

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信