# 一、call的实现
# 1.1call实现的主要思路
- 将传入的context添加该函数 ( context.fn = this )
- 执行该函数 ( context.fn() )
- 删除该函数 ( delete context.fn )
# 1.2注意细节
- 当call的参数为null时,会绑定到window上 ( var context = context || window )
- 传参数问题
- 通过for循环取出从1到最后一个的arguments,因为第0个是要绑定的参数
- 取出的参数存入数组
arg
中 - 通过
eval
解析并执行
# 1.3代码实现(es5版)
Function.prototype.mycall = function (context) {
var context = context || window;
context.fn = this
var args = []
for (var i = 1; i < arguments.length; i++) {
args.push('arguments[' + i + ']')
}
var result = eval('context.fn(' + args + ')')
return result
}
var obj = {
name: 'qmj',
age: 18
}
var name = 'hahh'
var age = 100
function foo(name, age) {
console.log(this.name, this.age, name, age)
}
foo.mycall()
# 1.4ES6版
Function.prototype.mycall = function(context){
context = context || window
context.fn = this;
let args = [...arguments].slice(1)
let result = context.fn(...args)
delete context.fn
return result
}
# 二、实现apply
# 2.1实现思路
apply实现的思路与call实现的思路差不多,区别是传参数的方法有点不同
- 1.添加函数到context的属性上
- 2.判断有无传入数组,没传数组:直接执行;传入数组:取出数组item存入args,用eval解析执行
# 2.2代码实现(es5版)
Function.prototype.myapply = function(context, arr) {
var conetxt = context || window;
context.fn = this
if(!arr) {
context.fn()
}else {
let args = []
for(let i = 0; i<arr.length; i++) {
args.push('arr['+ i + ']') // 存进去的是arr[0],arr[1]...
}
eval('conetxt.fn('+ args + ')')
}
}
var obj = {
name: 'qmj',
age: 18
}
function foo(name,age) {
console.log(this.name,this.age,name,age)
}
foo.myapply(obj,['xjp',55])
# 2.3ES6版
Function.prototype.apply = function(content = window) {
content.fn = this;
let result;
// 判断是否有第二个参数
if(arguments[1]) {
result = content.fn(...arguments[1]);
} else {
result = content.fn();
}
delete content.fn;
return result;
}
# 三、实现bind
: : : tip
bind方法会场见一核心函数。当这个形函数被调用时,bind()的第一个参数将作为他运行时的this,之后的一序列参数将会在传递的实参前传入作为他的参数
: : :
Function.prototype.bind = function(content) {
if(typeof this != 'function') {
throw Error('not a function');
}
let _this = this;
let args = [...arguments].slice(1);
return function F() {
// 判断是否被当做构造函数使用
if(this instanceof F) {
return _this.apply(this, args.concat([...arguments]))
}
return _this.apply(content, args.concat([...arguments]))
}
}
# 三、实现instanceof
# 3.1instanceof
原理:
- 右边的prototype是否在左边的原型链上
# 3.2 instanceof使用的例子:
// 例子一
var arr [1,2,3]
arr instanceof Array // true
arr instanceof Object // true
arr instanceof Function // false 因为不在该原型链上
// 例子二
let foo = function() {
console.log('hello javascript')
}
foo instanceof Function // true
foo instanceof Object // true
# 3.3 实现instanceof
function instance_of(L,R) {
let O = R.prototype
while(L.__proto__) {
if(L.__proto__ === O) {
return true
}else{
L = L.__proto__
}
}
return false
}
// 测试1
let arr = [1,2,3]
console.log(instance_of(arr,Array)) // true
console.log(instance_of(arr,Object)) // true
console.log(instance_of(arr,Function)) // false
// 测试2
let foo = function() {
console.log('nihao')
}
console.log(instance_of(foo,Array)) // false
console.log(instance_of(foo,Object)) // true
console.log(instance_of(foo,Function)) // true
# 四、实现typeof
function getType(temp) {
var str = temp.constructor.toString();
console.log(str)
return str.slice(9, str.indexOf('(')).toLowerCase();
}
# 五、手写防抖/节流函数
# 1.防抖
# 七、手写深/浅拷贝
function deepCopy(obj){
//判断是否是简单数据类型,
if(typeof obj == "object"){
//复杂数据类型
var result = obj.constructor == Array ? [] : {};
for(let i in obj){
result[i] = typeof obj[i] == "object" ? deepCopy(obj[i]) : obj[i];
}
}else {
//简单数据类型 直接 == 赋值
var result = obj;
}
return result;
}
# 八、reduce/map/filter
# reduce
Array.prototype.myReduce = function(cb, initValue) {
if (!Array.isArray(this)) {
throw new TypeError("not a array")
}
// 数组为空,并且有初始值,报错
if (this.length === 0 && arguments.length < 2) {
throw new TypeError('Reduce of empty array with no initial value')
}
let arr = this
let res = null
// 判断有没有初始值
if (arguments.length > 1) {
res = initValue
} else {
res = arr.splice(0,1)[0] //没有就取第一个值
}
arr.forEach((item, index) => {
res = cb(res, item, index, arr) // cb 每次执行完都会返回一个新的 res值,覆盖之前的 res
})
return res
};
// 测试结果
let arr = [1,2,3,4]
let result = arr.myReduce((res, cur) => {
return res + cur
})
console.log(result) // 10
# 九、手写一个new
# 9.1new之后做了什么呢?
- 创建了一个新对象
- 将新对象与构造函数原型连接起来(继承属性)
- 将构造函数的this指向这个对象、
- 返回这个新对象
# 9.2实现思路
很简单,只要完成上面四个步骤,就可以实现new了
# 9.3实现代码
/**
* 创建一个new操作符
* @param {*} Con 构造函数
* @param {...any} args 忘构造函数中传的参数
*/
function createNew(Con, ...args) {
let obj = {} // 创建一个对象,因为new操作符会返回一个对象
Object.setPrototypeOf(obj, Con.prototype) // 将对象与构造函数原型链接起来
// obj.__proto__ = Con.prototype // 等价于上面的写法
let result = Con.apply(obj, args) // 将构造函数中的this指向这个对象,并传递参数
return result instanceof Object ? result : obj
}