挑战大厂系列文章,转载请注明来源解析 new()、Object.create()、instanceof、call()、apply()、bind()等6个方法函数的原理和内部实现
new() 思路:把原型指向原型对象去获取方法,用 call/apply 去改 this 指向获取属性
function new(fn, ...args) {
/*
原型:__proto__
原型对象:prototype
可用此方法替代:let obj = Object.create(fn.prototype)
*/
// 1. 创建一个空对象,将这个对象的原型指向构造函数的原型对象
let obj = {
__proto__: fn.prototype
}// 2. 执行构造函数,把里面的 this 指到这个对象
let res = fn.apply(obj, args)// 3. 返回值为 object 类型则作为 new 方法的返回值返回,否则返回这个空对象
return res instanceof Object ? res : obj
}
Object.create() 思路:创建一个空函数 F 作为中介去承接原型对象的方法,obj.__proto__ = F.prototype = fn.prototype
/*
constructor 挂在原型对象 prototype 的属性上
F.prototype = {
constructor: F
}把 fn.prototype 当做参数传进来
F.prototype = proto
F.prototype = fn.prototype = {
constructor: fn
}
F.prototype = {
constructor: fn
}所以修改原型对象后需要重新把 constructor 指回去
*/
function create(proto) {
let F = function () {}
F.prototype = proto
F.prototype.constructor = F
return new F()
}
instanceof 思路:右边变量的原型对象存在于左边变量的原型链上
原型链:obj.__proto__.__proto__ 这样一直往上找直到 null 为止形成的链条
function myInstanceof(left, right) {
/*
let proto = left.__proto__
es6 新写法用 Object.getPrototypeOf(left) 替代 left.__proto__
*/
let proto = Object.getPrototypeOf(left)while(true) {
if (proto === null) {
return false
}
// 此时左边变量的原型等于右边变量的原型对象
if (proto === right.prototype) {
return true
}
// 不等于就继续往原型链上找,left.__proto__ = left.__proto__.__proto__
proto = Object.getPrototypeof(proto)
}
}
call 思路:将要改变 this 指向的方法挂到目标 this 上执行并返回
Function.prototype.call = function (context = window, ...args) {
let fn = Symbol('fn')
context[fn] = this
let res = context[fn](...args)
delete context[fn]
return res
}
解析:
// 原理
let obj = {
name: 'obj',
fn: function() {
console.log(this.name)
}
}
obj.fn() // 此时里面的 this 指向 objFunction.prototype.call = function (obj, ...args) {
obj = {
fn: f
}
let res = obj.fn(...args)
delete obj.fn
return res
}
f.call(obj)
apply apply 和 call 是一样的,只是传的参数不一样,apply 传数组
Function.prototype.apply = function (context = window, arr) {
let fn = Symbol('fn')
context[fn] = this;
let res;
if (!arr) {
res = context[fn]();
} else {
res = context[fn](...arr);
}delete context[fn]
return res;
}
bind 思路:bind 利用 apply 来改变指针,Object.create 来克隆原方法的原型对象,最后返回一个新的函数
/*
1、可以指定this
2、返回一个函数
3、可以传入参数
4、柯里化
5、一个绑定函数也能使用new操作符创建对象:这种行为就像把原函数当成构造器,提供的 this 值被忽略,同时调用时的参数被提供给模拟函数
*/
if (!Function.prototype.bind) {
Function.prototype.bind = function(context, ...args) {
let fn = this
let bound = function () {
// 指定this
return fn.apply(
this instanceof bound ? this : context, // new 忽略
args.concat(...arguments)
)
}
bound.prototype = Object.create(this.prototype)
return bound
}
}
往期系列 挑战大厂第2篇-手动实现promise.all
挑战大厂第1篇-js树结构互转
认识更多前端道友,一起进阶前端开发 微信公众号:前端一锅煮
【javascript|挑战大厂第3篇-new、call、create等常见函数的内部实现】QQ群:前端一锅煮
推荐阅读
- 操作系统|[译]从内部了解现代浏览器(1)
- web网页模板|如此优秀的JS轮播图,写完老师都沉默了
- 程序员|【高级Java架构师系统学习】毕业一年萌新的Java大厂面经,最新整理
- JavaScript|vue 基于axios封装request接口请求——request.js文件
- vue.js|vue中使用axios封装成request使用
- JavaScript|JavaScript: BOM对象 和 DOM 对象的增删改查
- JavaScript|JavaScript — 初识数组、数组字面量和方法、forEach、数组的遍历
- JavaScript|JavaScript — call()和apply()、Date对象、Math、包装类、字符串的方法
- JavaScript|JavaScript之DOM增删改查(重点)
- 单片机|自学单片机好找工作吗(会单片机能找什么工作?)