原型
理解protoType, proto, constructor
原型链
面试题
- 概述构造函数,原型和实例的关系 每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。
- 原型链 那么我们让原型对象等于另一个实例的实例,此时原型对象将包含一个指向另一个原型的指针,相应的,另一个原型中也包含一个指向另一个构造函数的指针。假如另一个原型又是另一个类型的实例,如此层层递进,就构成实例与原型的链条。
- 也就是如图的蓝色这条线
new 基础
new 操作实现的功能
function Test(name) {
this.name = name
}
Test.prototype.sayName = function () {
console.log(this.name)
}
const t = new Test('yck')
console.log(t.name) // 'yck'
t.sayName() // 'yck'
通过以上例子可知
- new 通过构造函数 Test 创建出来的实例可以访问到构造函数中的属性
- new 通过构造函数 Test 创建出来的实例可以访问到构造函数原型链中的属性,也就是说通过 new 操作符,实例与构造函数通过原型链链接了起来
如果构造函数有 return,会有什么影响
返回原始值不会生效,返回对象会导致 new 操作符没有作用。
- 如果返回原始值
function Test(name) {
this.name = name
return 1
}
const t = new Test('yck')
console.log(t.name) // 'yck'
构造函数如果返回原始值(虽然例子中只有返回了 1,但是你可以试试其他的原始值,结果还是一样的),那么这个返回值毫无意义
- 返回对象
function Test(name) {
this.name = name
console.log(this) // Test { name: 'yck' }
return { age: 26 }
}
const t = new Test('yck')
console.log(t) // { age: 26 }
console.log(t.name) // 'undefined'
构造函数如果返回值为对象,那么这个返回值会被正常使用
总结:构造函数尽量不要返回值。因为返回原始值不会生效,返回对象会导致 new 操作符没有作用。
调用 new 时会发生什么?
- 生成一个新对象
- 链接到原型
- 绑定 this
- 返回新对象
手写 new 操作符
首先我们再来回顾下 new 操作符的几个作用
- new 操作符会返回一个对象,所以我们需要在内部创建一个对象
- 这个对象,也就是构造函数中的 this,可以访问到挂载在 this 上的任意属性
- 这个对象可以访问到构造函数原型上的属性,所以需要将对象与构造函数链接起来
- 返回原始值需要忽略,返回对象需要正常处理
function create(Con,...args) {
// 首先函数接受不确定数量的参数,第一个参数为构造函数,其他参数为构造函数所用
// 内部创建一个空对象
let obj = {};
// 因为obj需要访问到构造函数原型链上的属性,所以通过原型量将两者联系起来
// 下面这两句的效果是一样的
// obj.__proto__ = Con.prototype;
Object.setPrototypeOf(obj,Con.prototype);
// 将this指向obj对象
let result = Con.call(obj, ...args);
// 判断构造函数返回的是否是函数,如果不是,则返回obj,这样实现了,忽略构造函数返回原始值的操作
return result instanceof Object ? result : obj;
}
function Test(name) {
this.name = name;
}
let a = create(Test,'li');
console.log(a);
通过 new 的方式创建对象和字面量创建有什么区别
通过new Object的方式,会调用Object构造函数,但是字面量不会调用构造函数
在调用Object方法的过程中,涉及到,要在原型链上找到Object这个方法,然后生成调用函数的堆栈,执行完该函数后,还要释放堆栈,综上,字面量要比new方法效率高且简洁