Lily
对 Lucy
说Hello
const lily= {
name: 'Lily',
sayHello(helloWord, other){
console.log(`${this.name} say ${helloWord} to ${other}`)
}
}
lily.sayHello("hello", "Lucy")
// Lily say Hello to Lucy
那换做其他对象怎么复用sayHello
方法呢
const jack = {
name:'Jack'
}
lily.sayHello.call(jack, 'hello', 'Lilei')
// Jack say Hello to Lilei
lily.sayHello.apply(jack,[ 'hello', 'Lilei'])
// Jack say Hello to Lilei
let say2 = lily.sayHello.bind(jack,'hello')
say2('Lilei')
// Jack say Hello to Lilei
say2('David')
// Jack say Hello to David
下面实现call
、 apply
、bind
的方法
Function.prototype.myCall = function(target,...args){
target = target || window
//定义一个不会重复的key,作为目标对象target的属性key
const funKey= Symbol()
//this即被调用的函数或对象,将this变成目标对象target属性key的属性值
target[funKey] = this
const res = target[funKey](...args)
delete target[funKey] // 执行完借用的函数后,删除掉
return res
}
// 区别就是这里第二个参数直接就是个数组
Function.prototype.myApply = function(target,args){
target = target || window
const funKey= Symbol()
target[funKey] = this
const res = target[funKey](...args)
delete target[funKey]
return res
}
// bind返回的是个函数,需要执行
Function.prototype.myBind = function (target,...outArgs) {
target = target || {}
const funKey = Symbol()
target[funKey] = this
return function (...innerArgs) { // 返回一个函数
const res = target[funKey](...outArgs, ...innerArgs)
//不能删除,闭包函数(偏函数)
// delete target[funKey]
return res
}
}
从上面的myBind
的实现看;bind
涉及到闭包,下面说下闭包、偏函数、柯里化
什么是闭包?
function generate() {
const a = 1;
//注意需要return内部函数,才回形成闭包
return function () {
// a这个变量不在当前作用域,于是它是一个自由变量。
// 引用了自由变量的函数称为闭包。
console.log(a++);
};
}
generate
作为高阶函数
返回了一个新的函数,该函数引用了外部作用域中的变量a,于是该函数称为闭包函数。
- 闭包就是:引用了自由变量的函数。
- 自由变量:指在当前作用域引用但既没有定义在当前作用域也未定义在全局作用域,而是定义在外层的局部作用域中的变量。
所以在此明确几个关于闭包的要点:
- 闭包必然是定义在一个函数内部的,因为局部作用域只存于函数内。
- 局部作用域的变量无法直接从外部访问,只能通过作用域链访问,所以闭包是访问局部变量的桥梁。
- 闭包造成的直接结果是自由变量不被垃圾回收,所以自由变量可以缓存一些信息。
闭包的典型应用场景有两个:
- 设置私有变量(基于局部变量的不可访问性)
- 柯里化和偏函数(基于自由变量的不回收性)
私有变量就是那些函数可以调用但是外部无法直接获取的变量。比如下面这个场景:
function User() {
// 这里的_password承担着私有变量的作用
let _password;
return class User {
constructor(username, password) {
this.username = username;
// 使用自由变量保存密码
_password = password;
}
login() {
console.log(this.username, _password);
}
};
}
const user = new (User())("diana", "password");
user.login();
- 我们希望隐藏用户的密码,只允许内部访问,此时就可以使用自由变量作为“私有变量”,返回一个闭包进行访问。
- 局部变量的外部不可访问性是关键,本质上这一切都来自于JS使用的词法作用域模型,变量只能从内向外单向查找。
柯里化是一种操作技巧,在编程中指的是:将接受n个参数的单个函数转换为接受单个参数的n个函数。比如下面这个场景:
function info(country, province, city) {
console.log(country + "-" + province + "-" + city);
}
我们想要打印城市信息,对于某个省的城市我们需要重复的输入country,province这两个字段,此时我们可以对该函数进行柯里化
改造:
function info(country) {
return function (province) {
return function (city) {
console.log(country + "-" + province + "-" + city);
};
};
}
const province = info("中国")("浙江省");
province("杭州市");
province("温州市");
- 我们将接受三个参数
country,province,city
的函数转换为三个只接受单个参数的函数,然后根据需要随时生成一个已经内置”部分参数“的函数。 - 这么做最大的好处是提高了代码的复用性,我们可以提前缓存一些参数,避免重复输入。
偏函数也是一种操作技巧,在编程中指的是:将接受n个参数的单个函数任意固定a个参数,返回接受剩余n-a个参数的函数。
偏函数和柯里化属于一类操作,区别在于:
- 柯里化强调函数只能接受“单参数”,有n个参数就要拆解为n个单参数函数;
- 偏函数更加“随意”,将接受n个参数的函数一分为二,任意固定一个或多个参数。
上面的例子如果使用偏函数:
function info(country) {
return function (province, city) {
console.log(country + "-" + province + "-" + city);
};
}
info("中国")("浙江省", "杭州市");
info("中国")("浙江省", "温州市");
柯里化和偏函数本质没什么区别,只是约束不同。能够实现这类操作的核心有两点:
- 在于“自由变量”的不回收性,由于垃圾回收不去销毁闭包中引用的自由变量,我们才能缓存部分参数;
- 在于JS使用静态的词法作用域,词法作用域的划分和查询根据”代码书写“的位置来决定。
对this对象的理解
作者:CUGGZ
链接:https://juejin.cn/post/6941194115392634888
来源:稀土掘金
this 是执行上下文中的一个属性,它指向最后一次调用这个方法的对象。在实际开发中,this 的指向可以通过四种调用模式来判断。
- 第一种是函数调用模式,当一个函数不是一个对象的属性时,直接作为函数来调用时,this 指向全局对象。
- 第二种是方法调用模式,如果一个函数作为一个对象的方法来调用时,this 指向这个对象。
- 第三种是构造器调用模式,如果一个函数用 new 调用时,函数执行前会新创建一个对象,this 指向这个新创建的对象。
- 第四种是 apply 、 call 和 bind 调用模式,这三个方法都可以显示的指定调用函数的 this 指向。其中 apply 方法接收两个参数:一个是 this 绑定的对象,一个是参数数组。call 方法接收的参数,第一个是 this 绑定的对象,后面的其余参数是传入函数执行的参数。也就是说,在使用 call() 方法时,传递给函数的参数必须逐个列举出来。bind 方法通过传入一个对象,返回一个 this 绑定了传入对象的新函数。这个函数的 this 指向除了使用 new 时会被改变,其他情况下都不会改变。
这四种方式,使用构造器调用模式的优先级最高,然后是 apply、call 和 bind 调用模式,然后是方法调用模式,然后是函数调用模式。