无法加载

深入了解JavaScript中的new运算符

FavoriteLoading收藏

Object类型是目前JavaScript中使用最多的一个类型,目前大家使用的大部分引用数据类型都是Object类型,Object类型的使用频率高是因为其对于数据存储和传输是非常理想的选择。由于引用数据类型的实例都需要通过new操作符来生成,因此我们需要了解new操作符的相关知识。

new操作符在执行过程中会改变this的指向,所以了解new操作符之前,我们先了解一下this的用法。

function Person(name, age) {
    this.name = name;
    this.age = age;
}
console.log(new Person('miaomiao', 18));//Person{name:"qhhh",age:18}

输出的结果包含了name和age的信息,事实上我们并未通过return返回任何值,为什么输出的信息中会包含name和age属性呢?其中起作用的就是this这个关键字了。

我们通过以下代码输出this,看看this的具体内容:

function Person(name,age){ 
    console.log(this); 
    this.name = name; 
    this.age = age; 
}
new Person('qhhh',18);

我们可以发现this的实际值是Person的空对象,后两句就相当于给Person对象添加name和age属性,结果 真的是这样吗?不如我们改写一下Person函数。

function Person(name,age){ 
    var Person = {}; 
    Person.name = name; 
    Person.age = age; 
}
console.log(new Person('qhhh',18));

我们可以发现输出的结果中并没有包含name和age属性,这是为什么呢?

因为在JavaScript中,如果函数没有return值,则默认return this。而上面代码中的this实际是一个Person空对象,name和age属性知识被添加到了临时变量Person中,为了能让输出结果包含name和age属 性,我们将临时变量Person进行return就行了。

function Person(name,age){ 
    var Person = {}; 
    Person.name = name; 
    Person.age = age; 
    return Person; 
}
console.log(new Person('qhhh',18));

最后的返回值中包含了name和age属性,通过以上的分析,我们了解了构造函数中this的用法,那么它与new操作符之间有什么关系呢?

我们先来看看下面这行简单的代码,该代码的作用是通过new操作符生成一个Person对象的实例。

var Person = new Person();

从表面上看这行代码的主要作用是创建一个Person对象的实例,并将这个实例值赋予person变量,person变量就会包含Person对象的属性和函数。

其实使用new操作符做了三件事情,如下代码所示:

第一行:创建一个空对象。

var person = {}; 
person.__proto__ = Person.prototype; 
Person.call(person);

第二行:将空对象的 __proto__ 属性指向Person对象的prototype属性。

第三行:将Person()函数中的this指向person变量。

于是person变量就是Person对象的一个实例。

我们自定义一个类似new功能的函数,来具体讲解上面的三行代码。

function Person(name,age){ 
    this.name = name; 
    this.age = age; 
}
function New(){ 
    var obj = {}; 
    var res = Person.apply(obj,arguments); 
    return typeof res === 'object' ? res : obj; 
}
console.log(New('qhhh',18));

返回的结果中包含name和age属性,这就证明了new运算符对this指向的改变,Person.apply(obj.arguments)调用后Person对象中的this就指向了obj对象,这样obj对象就具有了name和age 属性。

因此,不仅要关注new操作符函数本身,也要关注它的原型属性。

我们对上面的代码进行改动,在Person对象的原型链上增加一个sayHi()函数,然后通过New()函数返回的对象,去调用sayHi()函数,看看执行情况如何。

function Person(name,age){ 
    this.name = name; 
    this.age = age; 
}
Person.prototype.sayHi = function(){ 
    console.log('Hi'); 
}
function New(){ 
    var obj = {}; 
    var res = Person.apply(obj,arguments); 
    return typeof res === 'object' ? res : obj; 
}
console.log(New('qhhh',18)); 
console.log(New('qhhh',18).sayHi());

我们发现执行报错了,New()函数返回的对象并没有调用sayHi()函数,这是因为sayHi()函数是属于Person原型的函数,只有Person原型链上的对象才能继承sayHi()函数,那么我们应该怎样做呢?

这里需要用到的就是 __proto__ 属性,实例的 __proto__ 属性指向的是创建实例对象时,对应的 函数的原型。设置obj对象的 __proto__ 值为Person对象的prototype属性,那么obj对象就继承了Person原型 上的sayHi()函数,这样就可以调用sayHi()函数了。

function Person(name,age){ 
    this.name = name;  
    this.age = age; 
}
Person.prototype.sayHi = function(){ 
    console.log('Hi'); 
}
function New(){ 
    var obj = {}; 
    obj.__proto__ = Person.prototype; //核心代码,用于继承。 
    var res = Person.apply(obj,arguments); 
    return typeof res === 'object' ? res : obj; }// console.log(New('qhhh',18)); 
console.log(New('qhhh',18).sayHi());

留下评论

微信:15182814906

QQ:1548902957

邮箱:1548902957@qq.com