call 方法
call
方法的作用:改变某个函数运行时的 context
即函数的执行上下文。换句话说,就是改变函数体内部 this 的指向。
// 这里不能用箭头函数,否则this的指向会有问题(这里的this指调用的那个函数)
Function.prototype.my_call = function(context, ...argument) {
let ctx = context || window
ctx.fn = this // this赋值给ctx.fn,即ctx.fn指向原函数
return ctx.fn(...argument) // 执行ctx.fn,即ctx调用了原函数,此时原函数的this指向ctx
}
// 例子
const obj = {
name: '孙悟空',
showName(age) {
console.log(`${this.name}今年${age}岁`)
}
}
const newObj = {
name: '诸葛亮',
}
obj.showName.my_call(newObj, 18) // 诸葛亮今年18岁
拓展补充(基础好的朋友请绕过~)
有人说,网上有很多种写法,和你这个传的参数不一样,甚至不用传参数。其实我想说,都一样的,归根到底都是用来应付面试的,在实际开发过程中,根本用不到手写原生就有的方法~~当然对你有意义有帮助的是手写的过程的一个思路。
// 当前已知传了一个参数context,思路是一样的,我们要想的是如何获取剩余参数
Function.prototype.my_call = function(context) {
const ctx = context || window
// arguments是一个类数组对象,可以使用Array.from方法转为真正的数组,
// 这样才得以使用数组原型上的方法shift。目前是假定不能使用call/apply,假如可以使用的话,
// 可以用Array.prototype.slice.call(arguments, 1)来获取剩余参数
const args = Array.from(arguments) // 简单理解为args就是arguments
args.shift() // shift方法调用后会影响原数组,所以args就是剩余参数
ctx.fn = this
return ctx.fn(...args)
}
// 无参数情况,思路是一样的,我们要想如何获取第一个参数和剩余参数
Function.prototype.my_call = function() {
const args = Array.from(arguments)
const ctx = args.shift() // 获取第一个参数ctx,此时调用了shift方法,args就是剩余参数
ctx.fn = this
return ctx.fn(...args)
}
// 只要思路清晰,变形是多种多样的
Function.prototype.my_call = function() {
const ctx = Array.prototype.shift.call(arguments) // 获取第一个参数ctx,此时调用了shift方法,arguments就是剩余参数
ctx.fn = this
return ctx.fn(...arguments)
}
Function.prototype.my_call = function() {
const [ctx, ...args] = arguments // 使用拓展运算符解构赋值获取第一个参数ctx和剩余参数args
ctx.fn = this
return ctx.fn(...args)
}
对arguments
使用技巧总结:
- 获取第一个参数:
Array.prototype.shift.call(arguments)
或[].shift.call(arguments)
(注意 shift 方法会影响原数组) - 获取剩余参数:
Array.prototype.slice.call(arguments, 1) 或 [].slice.call(arguments, 1)
- 使用
Array.from(arguments)
转为数组后使用数组原型上的方法。 - 使用解构赋值
const [firstArg, secondArg, ...restArg] = arguments
apply 方法
apply
和call
的作用一样,只是传的参数不一样。
func.call(this, arg1, arg2, ...)
func.apply(this, [arg1, arg2, ...])
Function.prototype.my_apply = function(context, argument) {
let ctx = context || window
if (!Array.isArray(argument)) {
throw new Error('传入的第二个参数必须数组')
}
ctx.fn = this
return ctx.fn(...argument)
}
// 例子
const list = [1, 2, 3, 4, 5]
const min = Math.min.my_apply(Math, list)
console.log(min) // 1
new 操作符
function myNew(Constructor, ...arguments){
// 创建一个新的空对象
let obj = {}
// 将新对象的内部属性__proto__指向构造函数的原型,这样新对象就可以访问原型中的属性和方法
obj.__proto__ = Constructor.prototype;
// 取得构造函数的返回值
const ret = Constructor.call(obj, ...arguments)
// 如果返回值是一个对象就返回该对象,否则返回构造函数的一个实例对象
return typeof ret === 'object' ? ret : obj
}
instanceof 操作符
function my_instanceof(L, R) {
// L 表示左表达式,R 表示右表达式
// 取 R 的显示原型
const r = R.prototype
// 取 L 的隐式原型
let l = L.__proto__
while(true) {
if (l === null) return false
if (l === r) return true
l = l.__proto__
}
}
map 方法
Array.prototype.my_map = function (fn, context) {
// 判断第一个参数是否是函数
if (typeof fn !== 'function') {
throw new Error(`${fn} is not a function`)
}
let result= []
// 定义上下文
const ctx = context || this
// 将回调结果放入数组中
this.forEach((item, index) => {
result.push(fn.call(ctx, item, index, this))
})
return result
}
filter 方法
Array.prototype.my_filter = function (fn, context) {
// 判断第一个参数是否是函数
if (typeof fn !== 'function') {
throw new Error(`${fn} is not a function`)
}
// 定义上下文
const ctx = context || this
let result = []
// 将回调结果放入数组中
this.forEach((item, index) => {
if (fn.call(ctx, item, index, this)) {
result.push(item)
}
})
return result
}
forEach 方法
Array.prototype.my_forEach = function (fn, context) {
// 判断第一个参数是否是函数
if (typeof fn !== 'function') {
throw new Error(`${fn} is not a function`)
}
// 定义上下文
const ctx = context || this
for (let i = 0, len = this.length; i < len; i++) {
fn.call(ctx, this[i], i, this)
}
}
some 方法
Array.prototype.my_some = function(fn, context) {
// 判断第一个参数是否是函数
if (typeof fn !== 'function') {
throw new Error(`${fn} is not a function`)
}
// 定义上下文
const ctx = context || this
let result = false
this.forEach((item, index) => {
if(fn.call(ctx , item, index, this)){
result = true
}
})
return result
}
every 方法
Array.prototype.my_every = function(fn, context) {
// 判断第一个参数是否是函数
if (typeof fn !== 'function') {
throw new Error(`${fn} is not a function`)
}
// 定义上下文
const ctx = context || this
let result = false
this.forEach((item, index) => {
if(fn.call(ctx, item, index, this)){
result = true
} else {
result = false
}
})
return result
}
deepClone 深克隆
// 乞丐版,当被克隆对象的属性值类型为Symbol、Function时克隆后新对象会丢失这些类型
const deepClone = target => JSON.parse(JSON.stringify(target))
const isObject = obj => typeof obj === 'object' && obj !== null // 判断是否为对象,注意typeof null 为 object 需要排除
const deepClone = (target, map = new WeakMap()) => { // target 为被深克隆的对象,map用于处理互相引用的情况,用WeakMap而不是Map是因为WeakMap是弱引用,有利于垃圾回收
if (!isObject(target)) return target // 判断不是对象则返回自己
if (map.get(target)) return map.get(target) // 处理相互引用
const cloneTarget = Array.isArray(target) ? [] : {} // 初始化新对象时,注意数组和对象类型
map.set(target, cloneTarget)
// 用Reflect.ownKeys方法可以处理Symbol类型的属性,用Object.keys则不行
Reflect.ownKeys(target).forEach(key => {
// 为新对象进行赋值,先判断是否是对象,若是对象则递归调用
cloneTarget[key] = isObject(target[key]) ? deepClone(target[key]) : target[key]
})
return cloneTarget
}
ajax 请求
const ajax = (method, url, data) => {
const xhr = new XMLHttpRequest()
xhr.open(method, url, true)
xhr.onreadystatechange = () => {
if (xhr.readystate === 4 && xhr.status === 200) {
console.log(xhr.responseText)
}
}
xhr.send(data)
}