IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    JavaScript - this、call、apply、bind

    Johon发表于 2022-07-10 00:00:00
    love 0
    #### this 与其他语言相比,函数的 `this` 关键字在 `JavaScript` 中的表现略有不同,此外,在[严格模式](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Strict_mode)和非严格模式之间也会有一些差别。 在绝大多数情况下,函数的调用方式决定了 `this` 的值(运行时绑定)。`this` 不能在执行期间被赋值,并且在每次函数被调用时 `this` 的值也可能会不同。ES5 引入了 bind 方法来设置函数的 `this` 值,而不用考虑函数如何被调用的。ES2015 引入了[箭头函数](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Functions/Arrow_functions),箭头函数不提供自身的 this 绑定(`this` 的值将保持为闭合词法上下文的值)。 `this` 的值取决于它出现的上下文:函数、类或全局。 ##### 全局作用域 - 直接调用一个函数,就是默认绑定,this 在非严格模式下指向的为全局对象 `window` ```javascript title="全局作用域" function fn() { console.log(this) // window } fn() ``` ```javascript title="全局作用域" const obj = { fn: () => { console.log(this) // window }, fn1() { return function () { console.log(this) // window } }, fn2() { console.log(this) // window }, } obj.fn() obj.fn1()() const Fn = obj.fn2 Fn() ``` ##### 函数作用域 - 函数 fn 作为一个对象的方法来调用时,this 指向这个对象 ```javascript title="普通函数" const obj = { fn() { console.log(this) // obj }, } obj.fn() ``` - 使用 new 调用时,函数执行前会新创建一个对象,this 指向这个新创建的对象。 ```javascript title="构造函数" function fn() { console.log(this) // fn } let obj = new fn() ``` ```javascript title="构造函数" function fn() {} fn.prototype = { func: function () { console.log(this) // fn }, } let FN = new fn() FN.func() const obj = { name: 'j', } FN.func.call(obj) // obj FN.func.apply(obj) // obj // 在使用 call,apply,bind 改变 this,这个优先级仅次于 new ``` ##### 箭头函数 - 箭头函数中的 `this` 是词法作用域,它继承自外部作用域中的 `this` 。这意味着箭头函数的 `this` 和定义它的上下文中的 `this` 是一样的 ```javascript title="箭头函数" const obj = { name: 'Alice', greet() { const arrowFunction = () => { console.log(this.name) } arrowFunction() }, } obj.greet() // 输出 "Alice" ``` #### call [**Function.prototype.call(thisArg, arg1, arg2, ...)**](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/call) - `call()` 方法使用一个指定的 `this` 值和单独给出的一个或多个参数来调用一个函数。 - 该方法的语法和作用与 `apply()` 方法类似,只有一个区别,就是 `call()` 方法接受的是一个参数列表,而 `apply()` 方法接受的是一个包含多个参数的数组 **使用** ```javascript title="一个栗子" function method(a, b) { console.log(this, a, b) } method.myCall(null, 1, 2) ``` **具体实现** - 不传入第一个参数,那么默认为 `window` - 改变了 this 指向,让新的对象可以执行该函数。那么思路可以变成给新的对象添加一个函数,然后在执行完以后删除 ```javascript title="call" Function.prototype.myCall = function (context, ...args) { context = context === null || context === undefined ? globalThis : Object(context) // 使用Symbol确保不会出现命名冲突 let key = Symbol('_key') // 使用defineProperty创建一个属性存放 this Object.defineProperty(context, key, { value: this }) // 运行方法 let result = context[key](...args) // 删除方法 delete context[key] return result } ``` #### apply [**Function.prototype.apply(thisArg, [argsArray])**](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/apply) - `apply()` 方法调用一个具有给定 `this` 值的函数,以及以一个数组(或一个类数组对象)的形式提供的参数。 **使用** ```javascript title="一个栗子" function method(a, b) { console.log(this, a, b) } method.apply({}, [2, 3]) ``` **具体实现** - `apply` 的实现和 `call` 类似 ```javascript title="apply" Function.prototype.myApply = function (context) { context = context === null || context === undefined ? globalThis : Object(context) // 使用Symbol确保不会出现命名冲突 let key = Symbol('_key') // 使用defineProperty创建一个属性存放 this Object.defineProperty(context, key, { value: this }) // 需要判断是否存储第二个参数 // 如果存在,就将第二个参数展开 let result = arguments[1] ? context[key](...arguments[1]) : context[key]() // 删除方法 delete context[key] return result } ``` #### bind [**Function.prototype.bind(thisArg, arg1, arg2, ...)**](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/bind) - `bind()` 方法创建一个新的函数,在 `bind()` 被调用时,这个新函数的 `this` 被指定为 `bind()` 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。 **使用** ```javascript title="一个栗子" function method(a, b) { console.log(this, a, b) } let newMethod = method.bind({}) //this指向改变为obj2 newMethod(3, 4) ``` **具体实现** - `bind` 和其他两个方法作用也是一致的,只是该方法会返回一个函数。并且我们可以通过 `bind` 实现柯里化。 ```javascript title="bind" Function.prototype.myBind = function (context) { if (typeof this !== 'function') { throw new TypeError('Error') } let _this = this let args = [...arguments].slice(1) // 返回一个函数 return function F() { // 因为返回了一个函数,我们可以 new F(),所以需要判断 if (this instanceof F) { return new _this(...args, ...arguments) } return _this.apply(context, args.concat(...arguments)) } } ```


沪ICP备19023445号-2号
友情链接