🎉call()
💕call()的参数
thisArg:在调用 func 时要使用的 this 值
arg1, …, argN (可选) 函数的参数
✨call()的描述:
首先声明 func是一个函数,person是一个对象
针对这段代码:func.call(person,‘a1’,‘a2’)
调用func方法并传递两个参数’a1’,‘a2’ ,以及把func中的this设置为person对象
🍧call()的代码解释
function greet (a,b) {console.log(this)console.log(this.animal, "的睡眠时间一般在", this.sleepDuration, "之间",a, b)}const obj = {animal: "猫",sleepDuration: "12 到 16 小时",}greet.call(obj, "哦", "!!!") // {animal: '猫', sleepDuration: '12 到 16 小时'}//猫 的睡眠时间一般在 12 到 16 小时 之间 哦 !!!
🎡手写Call() :myCall()
❤️步骤
手写Call() 分为四步
- 定义myCall方法,加在Function原型上,使得所有函数都能点出来使用
- 设置this并调用原函数
- 接收剩余参数并返回结果
- 使用Symbol调优
🎶前置知识 this指向问题
全局执行环境中,指向全局对象window(非严格模式、严格模式)
函数内部,取决于函数被调用的方式
2.1. 直接调用的this值:
- 非严格模式:全局对象(window)
- 严格模式:undefined
2.2 对象方法调用的this值:
- 调用者
开启严格模式
- 脚本开启: ‘use strict’
- 函数内部开启:‘use strict’
- 注意:'use strict’写在代码顶端
// ------------- 1.全局执行环境 -------------// 严格模式,非严格模式 全局对象(window)// 'use strict'// console.log(this)// ------------- 2.函数内部 -------------// 2.1 直接调用-非严格模式// function func() {// console.log(this) // window// }// func()// 2.1 直接调用-严格模式// function func() {// 'use strict'// console.log(this) // undefined// }// func()// 2.2 对象方法调用const food = {name: '猪脚饭',eat() {'use strict'console.log(this)}}// 非严格模式,严格模式food.eat() // 调用者 Object {eat: ƒ eat(),name: "猪脚饭"}
再来看一下MDN的解释 MDN地址
✨下面这点很重要
o.f() 就使得 函数 f 中的 this 指向 对象 o
先定义一个对象o,在定义一个函数independent(),然后把函数追加到对象o中和上述可以实现一样的效果
🎀第一步:定义myCall方法,加在Function原型上,使得所有函数都能点出来使用
在Function对象的原形上通过"."的方式添加myCall属性,并给这个对象赋值一个函数
Function.prototype.myCall = function () {console.log("this is myCall")}function greet () { }greet.myCall()// this is myCall
🎶第二步:设置this并调用原函数
🎐图解
由于给person多加了一个f属性,所以后面需要使用 delete关键词 把f属性删掉
🎏代码
Function.prototype.myCall = function (thisArg) {thisArg.f = this //这个this就是原函数func(因为 根据前置知识的讲解 func.mycall()使得函数myCall的this指向func )// ,这段代码是在person(thisArg在这里就是person)对象上面增加一个属性,属性名为f 属性值为func(){...}thisArg.f()//根据前置知识 f的this是thisArg,在这里f是func 这样就完成了第二步}// ------------- 测试代码 -------------const person = {name: 'zhangsan'}function func (numA, numB) {console.log(this)console.log(numA, numB)return numA + numB}const res = func.myCall(person) // {name: 'zhangsan', f: ƒ}
🎄第三步:接收剩余参数并返回结果
使用…args接收剩余参数,并用解构赋值的方法把参数传递给调用者
Function.prototype.myCall = function (thisArg, ...args) {thisArg.f = this const res = thisArg.f(...args) //args=>[2,8] ...args=>2,8 把剩余参数传给func (numA=2,numB=8)delete thisArg.f //删除person中新加的f属性return res}// ------------- 测试代码 -------------const person = {name: 'zhangsan'}function func(numA, numB) {console.log(this) //Object{name:zhangsan}console.log(numA, numB) //2 8return numA + numB}const res = func.myCall(person, 2, 8)console.log('返回值为:', res) // 返回值为: 10
🍿 测试
Function.prototype.myCall = function (thisArg, ...args) {thisArg.f = thisconst res = thisArg.f(...args) delete thisArg.f return res}// ------------- 测试代码 -------------const student = {name: 'lisi'}function func2 (a, b, c) {console.log(this)console.log(a, b, c)return a + b + c}const res2 = func2.myCall(student, 1, 2, 3)console.log('返回值:', res2)
✨第四步:使用Symbol调优
🎶关于Symbol
Symbol的MDN链接
symbol 是一种基本数据类型(primitive data type)。
Symbol() 函数会返回 symbol类型的值,该类型具有静态属性和静态方法。每个从 Symbol() 返回的 symbol 值都是唯一的。一个 symbol 值能作为对象属性的标识符;
🍧symbol的使用
直接使用Symbol()创建新的 symbol 类型,并用一个可选的字符串作为其描述。这个描述只是为了看着方便没有实际用处。(MDN解释:对 symbol 的描述,可用于调试但不是访问 symbol 本身。)
var sym1 = Symbol();
var sym2 = Symbol("foo");
var sym3 = Symbol("foo");
// 这三个都不相等
Function.prototype.myCall = function (thisArg, ...args) {const key = Symbol('key')// 使用Symbol创建一个唯一的symbol值 作为对象的标识符// thisArg.key 是给thisArg对象增加一个名为字符串'key'的属性thisArg[key] = this //thisArg[key] 是把key作为一个变量 实际传过去的是Symbol('key')const res = thisArg[key](...args)delete thisArg[key]return res}
🎉测试
Function.prototype.myCall = function (thisArg, ...args) {const key = Symbol('key')// 使用Symbol创建一个唯一的symbol值 作为对象的标识符// thisArg.key 是给thisArg对象增加一个名为字符串'key'的属性thisArg[key] = this //thisArg[key] 是把key作为一个变量 实际传过去的是Symbol('key')const res = thisArg[key](...args)delete thisArg[key]return res}// ------------- 测试代码 -------------const student = {name: 'lisi'}function func2 (a, b, c) {console.log(this)console.log(a, b, c)return a + b + c}const res2 = func2.myCall(student, 1, 2, 3)console.log('返回值:', res2)
这个是谷歌浏览器的结果,谷歌这点有点显示bug
Edge就没有,下图是Edge浏览器的结果