new的作用及其手写
new的原理和想法其实很简单,new帮我们完成创建空对象,绑定目标对象的原型等操作.也就是说通过 new 操作符,实例与构造函数通过原型链连接了起来
就这样
function newF() {
let obj =new Object()//{};
let Constructor = [].shift.call(arguments);
obj.__proto__=Constructor.prototype;
let result =Constructor.apply(obj, arguments);
return typeof result ==='object'? result : obj
}
哎哎先别急着关
这代码第一次确实不是给人看的,或许第二次也不是,没事我们一行一行的看
function newF() {
let obj = {}; // 创建一个新的对象
// 取出第一个参数,该参数就是我们将会传入的构造函数,比如在调用new(P)的时候,Constructor就是P本身
// arguments(传入的参数)会被shift去除第一个参数,剩余的就是构造器P的参数
let Constructor = [].shift.call(arguments);(1)
// 将obj的原型指向构造函数,此时obj可以访问构造函数原型中的属性
obj.__proto__ = Constructor.prototype;(2)
// 改变构造函数的this的指向,使其指向obj, 此时obj也可以访问构造函数中的属性了
let result = Constructor.apply(obj, arguments);(3)
// 确保 new 出来的是个对象 返回的值是什么就return什么
return typeof result === 'object' ? result : obj (4)
}
还是一脸懵?我们在接着分析
- 1·arguments?
arguments 是一个类数组对象。代表传给一个function的参数列表,什么是类数组,简单理解就是具有数组的某些特性,但是不具有数组的api,我们在这里接收的参数中,第一个参数是我们的构造函数,就类似new Date()中的date(),但他并不是函数执行需要的参数,所以我们在这里将他去除。那可能有人会说你直接shift()不就好啦,还记得我刚刚提到的吗,类数组不具有数组的api,所以这里我们调用了[]的数组方法
Array.prototype.slice.call(arguments);
这才是将arguments转化为组数的方法^
- 2·拿——proto——和propotype赋值是几个意思
这个很好理解,因为我们每个实例对象的隐式原型——proto——都是指向创建他的构造函数的,这里我们要帮他指一下
- 3·不是讲new吗?apply是干嘛的
首先说一下作用,新对象本身没有构造函数的私有变量和方法,所以这里借用apply(1)改变this指向,执行构造函数的方法
- 4·为什么返回值还要三元判断
这个简单,因为构造函数会返回一个对象,所以在这里我们要判断一下
怎么实现new的过程中又加入了apply?
这是一个问题,因为在讲解一个概念又加入新的概念是很差的体验,但是删不得,那apply能干嘛呢
假如我们现在有一个
var person1 = {firstName: "Bill",lastName: "Gates",}
又有一个
var person = {
fullName: function() {return this.firstName + " " + this.lastName;}}
我们想取到person1的值该怎么办
很简单
person.fullName.apply(person1); // 将返回 "Bill Gates"
这就是apply的作用,改变this的指向,像他这么厉害的还有2个call和bind,他们实现的功能都差不多,这里我们尝试实现一下apply
Function.prototype.myApply = function (context, args) {
//这里默认不传就是给window,也可以用es6给参数设置默认参数
context = context || window
args = args ? args : []
//给context新增一个独一无二的属性以免覆盖原有属性
const key = Symbol()
context[key] = this //这里的this指向执行apply方法的对象本身,
//将自身赋值给传入对象的属性上,这样就可以执行传入对象的方法了
//通过隐式绑定的方式调用函数
const result = context[key](...args) //传参并执行
//需要对象本身在context的作用域里执对应的方法
//删除添加的属性
delete context[key]
//返回函数调用的返回值
return resul
}
结束了
并没有 我们做了一顿饭,但是还没吃
下面尝一下
function P(firstName, lastName) {
this.age=10;
this.getName=function() {
return`${firstName}${lastName}`;
};
}
function newF() {
let obj =new Object();
let Constructor = [].shift.call(arguments);
obj.__proto__=Constructor.prototype;
let result =Constructor.apply(obj, arguments);
return typeof result ==='object'? result : obj
}
let p =newF(P, 'amanda', 'kelake');
p.getName();
// "amanda kelake"
真香
本文参考了至少以下文章,如有纰漏请帮忙指出^^