new的使用方法
new用来创建一个实例对象,其隐式原型__proto__
指向构造函数的原型prototype
1、在构造函数中使用new
// 构造函数
function Car(color, brand) {
this.color = color
this.brand = brand
this.age = 0
// 默认返回实例对象 this
}
// 原型方法
Car.prototype.getcolor = function () {
return this.color
}
Car.prototype.getbrand = function () {
return this.brand
}
const VW = new Car('white', '大众')
const BMW = new Car('red', '宝马')
console.log(VW, BMW);//Car { color: 'white', brand: '大众', age: 0 } Car { color: 'red', brand: '宝马', age: 0 }
用构造函数Car创建对象,该函数默认返回this
(也就是一个实例)
我们也可以指定构造函数的返回值,让他不返回this,而是返回指定的对象
const instance = { color: 'blue', brand: '奔驰' }
// 构造函数
function Car(color, brand) {
this.color = color
this.brand = brand
this.age = 0
return instance // 指定返回对象
}
const VW = new Car('white', '大众')
console.log(VW);// { color: 'blue', brand: '奔驰' }
2、在类中使用new
// 类
class Book {
constructor(color, name) {
this.color = color
this.bookname = name
this.page = 0
// 默认返回实例对象 this
}
getcolor = function () {
return this.color
}
getbrand = function () {
return this.brand
}
}
const b1 = new Book('white', '第一行代码')
const b2 = new Book('red', 'javascript')
console.log(b1, b2);//Book { color: 'white', bookname: '第一行代码', page: 0 } Book { color: 'red', bookname: 'javascript', page: 0 }
constructor
方法是类的默认方法,创建类的实例化对象时被调用。
我们也可以指定constructor的返回值
// 类
const instance = { color: 'blue', brand: '学电脑' }
class Book {
constructor(color, name) {
this.color = color
this.bookname = name
this.page = 0
return instance // 指定返回对象
}
}
const b1 = new Book('white', '第一行代码')
console.log(b1);// { color: 'blue', brand: '学电脑' }
原理分析
那么new关键字到底做了哪几件事情,如何实现new的功能?
- new首先可以返回一个实例对象
- 该对象的__proto__ 指向构造函数的prototype
- 该对象身上的属性是由构造函数执行而添加进去的
- 构造函数默认返回一个实例对象,但是也可以自己指定返回的对象
代码实现
实现方法一:
function Fn() {
this.age = 23
this.class = 3
}
function $new(Fn) {
const obj = {}
obj.__proto__ = Fn.prototype
obj.Fn = Fn
obj.Fn()
delete obj.Fn
return obj
}
const instance = $new(Fn)
console.log(instance);// Fn { age: 23, class: 3 }
分析:
- 先创建一个空的字面量对象
obj
,把obj的原型指向构造函数的原型 - 我们希望obj的age属性设置为23,class属性设置为3,如何实现呢?
- 只需要把
Fn
这个函数在obj
身上执行一下就可以了 - 所以,临时给obj加上Fn属性,一会调用Fn的时候,obj的age和class属性就被设置好了
- 最后得
删除
这个临时属性Fn
实现方法二:
function $new(Fn) {
const obj = {}
obj.__proto__ = Fn.prototype
Fn.apply(obj)
return obj
}
const instance = $new(Fn)
console.log(instance);// Fn { age: 23, class: 3 }
分析:
刚才在obj身上添加临时的Fn的目的:让Fn在执行的时候this指向obj(谁调用Fn,this就指向谁)
所以上面做了优化,直接用apply
方法改变this指向,免去了添加临时属性的操作
实现方法三:
function $new(Fn) {
const obj = Object.create(Fn.prototype)
Fn.apply(obj)
return obj
}
const instance = $new(Fn)
console.log(instance);// Fn { age: 23, class: 3 }
分析:
其实原型的挂载也可以用Object.create
来进一步简化代码,于是又少了一行代码
进一步完善
$new返回值问题
我们知道,new关键字有个特点:构造函数默认返回一个实例对象,但是也可以自己指定返回的对象
为了实现这个需求,我们对$new的返回值
做个区分:
function $new(Fn) {
const obj = Object.create(Fn.prototype)
const inner = Fn.apply(obj)// 检查Fn有没有定义返回值
return inner instanceof Object ? inner : obj// 定义了返回值就返回inner对象,默认返回实例对象obj
}
const instance = $new(Fn)
console.log(instance);// Fn { age: 23, class: 3 }
这里代码的核心就是会去检查Fn执行完成后的返回值,$new发现Fn中有返回值就抛出去,否则默认返回刚创建好的对象
$new传参问题
function $new(Fn,...arg) {
const obj = Object.create(Fn.prototype)
const inner = Fn.apply(obj,arg)
return inner instanceof Object ? inner : obj
}
const instance = $new(Fn,'abc','hahaha')
console.log(instance);// Fn { age: 23, class: 3 }
参数通过剩余运算符收集,传递给Fn