◆

JavaScript new 关键字详解与实现原理

2023-02-02 01:370

用法示例

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关键字详解
  • 上面代码通过 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关键字详解

如代码与图所示调用构造函数时,没有使用 new 命令,这时候构造函数就是普通函数,不会生成实例对象。 而由于函数的调用是在全局作用域所以 this.name 就是 windows.name,this 就是全局对象。

如何保证构造函数必须使用 new 命令调用

  • 方法一:构造函数内部使用严格模式,即第一行加上 use strict

由于严格模式中,函数内部的 this 不能指向全局对象,默认等于 undefined,导致不加 new 调用会报错(undefined.报错)

function Animal(name) {
  'use strict';
  this.name = name;
}

Animal('dog');
new关键字详解
  • 方法二:构造函数内部判断是否使用 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'));
new关键字详解

如下代码所示,return 是一个对象,返回的就是这个对象,不会返回实例对象了

function Animal(name) {
  this.name = name;
  return { a: 1 };
}

console.log(new Animal('dog'));
new关键字详解

非构造函数使用 new 命令

普通函数(内部没有 this 关键字的函数)使用 new 命令,如果 return 后面跟着一个**对象 **,则会返回 return 语句指定的对象;否则,就会不管 return 语句,返回空对象。

function animal(name) {
  return name;
}

console.log(new animal('dog')); // {}
console.log(animal('dog')); // dog
new关键字详解
function animal(name) {
  return {
    name,
    age: 8,
  };
}

console.log(new animal('dog'));
console.log(animal('dog'));
new关键字详解

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');

Object.create 创建实例对象

Object.create 详解

💬留言板0

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

PAGE LOAD / LIVEJavaScript new 关键字详解与实现原理加载中