{} 语法可以创建一个对象,但是通常我们需要创建一些类似的对象,这个时候通常使用构造函数和 new 操作符来实现

构造函数

构造函数也是常规函数,但是约定构造函数以大写字母开头,并且只能通过 new 关键字来执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
function Dog(name, age, six){
// 隐藏的操作
// let this = {};
this.name = name;
this.age = age;
this.six = six;

this.eat = function(){
console.log('吃吃吃');
}

// 隐藏的操作
// return this;
}

Dog.prototype.color = 'white';
Dog.prototype.say = function(){
console.log(this.name);
}

let dog = new Dog('苏西', 1, '雄性');

console.log(dog.name); // 苏西
console.log(dog.age); // 1
console.log(dog.six); // 雄性
console.log(dog.color); // white
dog.say(); // 苏西

new 操作符

  1. 当一个函数使用 new 操作符创建对象时,相当于以下几个操作
    • 创建一个空对象并分配给 this
    • 函数体执行
    • 返回 this
  2. 构造函数的目的在于实现对一类事物的创建复用代码

构造器的 return

  1. 通常来说,构造器没有 return 语句,构造器自动将属性写入 this 并作为结果返回
  2. 如果一个构造器有 return 语句,它将遵循以下规则
    • return 一个对象,则返回这个对象
    • return 一个原始类型,忽略掉并返回 this
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function Dog(){
this.name = '苏西';

return {name: '佩奇'};
}

let dog = new Dog;
console.log(dog.name); // 佩奇

function Cat(){
this.name = '苏西';

return null;
}

let cat = new Cat;
console.log(cat.name); // 苏西

构造器没有参数,可以省略其括号,new Dog() 等价于 new Dog

手动实现 new 构造符

  1. 创建对象
  2. 对象原型指向构造函数原型
  3. 执行函数
  4. 返回该对象
1
2
3
4
5
6
7
8
9
10
function New(){
let object = {};
// arguments不是数组,类数组对象,但可以使用并且shift改变原类数组对象,剔除掉第一个参数(函数)
let constructor = [].shift.call(arguments);
// 只有函数拥有prototype, 所有对象拥有__proto__
object.__proto__ = consturct.prototype;
constructor.apply(object, arguments);
return object;
}

  1. 创建对象
  2. 对象原型指向构造函数原型
  3. 执行函数
  4. 判断返回值是否为对象
  5. 是返回该返回值,否返回该对象
1
2
3
4
5
6
7
8
9
10
function New(){
let object = {};
let constructor = [].shift.call(arguments);
object.__proto__ = constructor.prototype;
let result = constructor.apply(object, arguments);
let type = typeof result;

// result是对象并且不是null
return (type === 'object' || type === 'function') && result ? result : object;
}
1
2
3
4
5
6
7
8
function New(F, ...args){
// 指定原型创建对象
let object = Object.create(F.prototype);
let result = F.apply(object, args);
let type = typeof result;

return result && (type === 'object' || type === 'function') ? result : object;
}
1
2
3
4
5
6
7
function New(F: () => any, ...args: any): any{
let object: any = Object.create(fn.prototype);
let result: any = F.apply(object, args);
let type: string = typeof result;

return result && (type === 'object' || type === 'function') ? result : object;
}

构造器模式检测

在一个函数内部可以使用 new.target 属性来检测是否使用 new 操作符进行了调用,对于使用了 new 关键字,该属性是该函数,否则为 undefined

1
2
3
4
5
6
function Dog(){
console.log(new.target);
}

new Dog(); // function Dog(){...}
Dog(); // undefined

可以使用 new.target 使常规模式调用函数和使用 new 操作符有相同的行为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function Dog(name){
if(!new.target){
return new Dog(name);
}

this.name = name;
}

// 第二种
function Dog(name){
if(!new.target){
// arguments.callee() 调用函数本身
return new arguments.callee(name);
}

this.name = name;
}