JavaScript new 关键字详解与实现原理
用法示例
function Animal(name) {
this.name = name;
}
Animal.prototype.run = function () {
console.log(this.name, 'is running...');
};
const small_dog = new Animal('dog');
console.log(small_dog);
// dog is running...
console.log(small_dog.run());

- 上面代码通过
new命令,让构造函数Animal生成一个实例对象,保存在变量small_dog中。 - 这个新生成的实例对象,从构造函数
Animal得到了name属性。 new命令执行时,构造函数内部的this,就代表了新生成的实例对象,this.name表示实例对象有一个name属性,值是传入的dog。small_dog.run()执行时实际上调用的是构造函数Animal的方法,其中的this,代表了新生成的实例对象,所以输出dog is running...
new 执行构造函数带括号与不带括号区别
new 命令本身就可以执行构造函数,所以后面的构造函数可以带括号,也可以不带括号,下面两行代码是等价的,但是为了表示这里是函数调用,推荐使用括号。
// 推荐的写法
var animal = new Animal();
// 不推荐的写法
var animal = new Animal();
对构造函数调用时忘记使用 new 命令,直接调用构造函数
function Animal(name) {
this.name = name;
}
const small_dog = Animal('dog');
console.log('small_dog:', small_dog);
console.log('name:', name);

如代码与图所示调用构造函数时,没有使用 new 命令,这时候构造函数就是普通函数,不会生成实例对象。 而由于函数的调用是在全局作用域所以 this.name 就是 windows.name,this 就是全局对象。
如何保证构造函数必须使用 new 命令调用
- 方法一:构造函数内部使用严格模式,即第一行加上
use strict
由于严格模式中,函数内部的
this不能指向全局对象,默认等于undefined,导致不加new调用会报错(undefined.报错)
function Animal(name) {
'use strict';
this.name = name;
}
Animal('dog');

- 方法二:构造函数内部判断是否使用
new命令
如果发现没有使用,则直接返回一个实例对象。
function Animal(name) {
if (!(this instanceof Animal)) {
return new Animal(name);
}
this.name = name;
}
console.log(Animal('dog').name); // dog
- 方法三:使用
new.target
允许你检测函数或构造方法是否是通过
new运算符被调用的。 在通过new运算符被初始化的函数或构造方法中,new.target返回一个指向构造方法或函数的引用。在普通的函数调用中,new.target的值是undefined。
function Animal(name) {
if (!new.target) {
throw new Error('请使用 new 命令调用!');
}
this.name = name;
}
console.log(Animal('dog').name); // VM78:3 Uncaught Error: 请使用 new 命令调用!
new 命令的实现原理
- 创建一个空对象,作为将要返回的对象实例。
var obj = {} 或者 var obj = new Object() - 将这个空对象的原型,指向构造函数的
prototype属性。obj.__proto__ = Animal.prototype - 将这个空对象赋值给函数内部的
this关键字。Animal.call(obj, 'dog');return obj - 开始执行构造函数内部的代码。
构造函数内部,
this指的是一个新生成的空对象,所有针对this的操作,都会发生在这个空对象上
new 构造函数时碰到了 return 关键字
如果构造函数内部有 return 语句,而且 return 后面跟着一个**对象 **,new 命令会返回 return 语句指定的对象;否则,就会不管 return 语句,返回 this 对象。
如下代码所示,return 是一个字符串,return 语句被忽略,返回的仍旧是实例对象
function Animal(name) {
this.name = name;
return '不是对象';
}
console.log(new Animal('dog'));

如下代码所示,return 是一个对象,返回的就是这个对象,不会返回实例对象了
function Animal(name) {
this.name = name;
return { a: 1 };
}
console.log(new Animal('dog'));

非构造函数使用 new 命令
普通函数(内部没有 this 关键字的函数)使用 new 命令,如果 return 后面跟着一个**对象 **,则会返回 return 语句指定的对象;否则,就会不管 return 语句,返回空对象。
function animal(name) {
return name;
}
console.log(new animal('dog')); // {}
console.log(animal('dog')); // dog

function animal(name) {
return {
name,
age: 8,
};
}
console.log(new animal('dog'));
console.log(animal('dog'));

new命令总是返回一个对象,要么是实例对象,要么是return语句指定的对象
模拟实现 new 方法
function _new(/* 构造函数 */ ctorFn) {
if (typeof ctorFn !== 'function') {
throw 'ctorFn must be a function';
}
// 将 arguments 对象转为数组
var args = [].slice.call(arguments);
// 取出构造函数
var constructor = args.shift();
// 创建一个空对象,继承构造函数的 prototype 属性
var context = Object.create(constructor.prototype);
// 执行构造函数
var result = constructor.apply(context, args);
// 如果返回结果是对象,就直接返回,否则返回 context 对象
return typeof result === 'object' && result != null ? result : context;
}
function Animal(name) {
this.name = name;
}
_new(Animal, '12312');

还没有留言,来发表第一条吧~