面试官:你能手写 bind 吗?——JS this 全家桶趣味深度剖析

你是否曾在 JS 的世界里被 this 搞得头晕脑胀?是否在面试时被问到 call/apply/bind/new 的底层原理时,脑袋一片空白?别慌!本文将带你用轻松幽默的方式,层层递进地揭开这些谜团,助你成为 JS 面试王者!😎

一、this 是什么?

this 是 JS 里一个神秘的关键字,指向当前函数的调用者。它能让代码更灵活,但也常常让人抓狂。this 的指向有四大规则:

  1. 默认绑定:独立调用,this 指向 window(浏览器)或 global(Node)。
  2. 隐式绑定:被对象调用,this 指向该对象。
  3. 显示绑定:call、apply、bind,this 指向绑定对象。
  4. new 绑定:构造函数调用,this 指向新创建的对象。

记住口诀:独立 window,对象对象,call/apply/bind 显示绑,new 新对象!

二、代码实战:this 的各种姿势

1. 默认绑定

javascript 复制代码
var a = 1
function foo() {
    var a = 2
    console.log(this.a)
}
function bar() {
    var a = 3
    foo()
}
bar() // 输出 1

分析:foo 独立调用,this 指向 window,window.a = 1,所以输出 1。

2. 隐式绑定

javascript 复制代码
var a = 2
function foo() {
    console.log(this.a)
}
let obj = {
    a: 1,
    foo: foo
}
obj.foo() // 输出 1

分析:obj.foo(),this 指向 obj,obj.a = 1。

3. 显示绑定(call/apply/bind)

call 实现

javascript 复制代码
Function.prototype.myCall = function(context, ...args) {
    context = context || window
    const fn = Symbol('fn')
    context[fn] = this
    const res = context[fn](...args)
    delete context[fn]
    return res
}

function foo(x, y) {
    console.log(this.a, x + y);
}
let obj = { a: 1 }
foo.myCall(obj, 1, 2) // 输出 1 3

apply 实现

javascript 复制代码
Function.prototype.myApply = function(context, ...args) {
    context = context || window
    const arg = args[0]
    if (!Array.isArray(arg)) {
        throw new TypeError('CreateListFromArrayLike called on non-object')
    }
    const fn = Symbol('fn')
    context[fn] = this
    const res = context[fn](...arg)
    delete context[fn]
    return res
}

bind 实现

javascript 复制代码
Function.prototype.myBind = function(context, ...args1) {
    let _this = this
    return function F(...args2) {
        if (this instanceof F) {
            return new _this(...args1, ...args2)
        } else {
            return _this.apply(context, [...args1, ...args2])
        }
    }
}

function foo(x, y, z) {
    console.log(this.a, x + y + z);
}
let obj = { a: 1 }
let bar = foo.myBind(obj, 1, 2)
bar(3) // 输出 1 6
let p = new bar(4, 5)
console.log(p)

小贴士:bind 返回的新函数可以被 new 调用,this 指向新对象,否则指向绑定对象。

4. new 绑定

javascript 复制代码
function Person() {
    return function F() {
        this.name = 'sss'
        return foo()
    }
}
function foo() {
    console.log('123');
}
let p = new Person()
let n = new p()
console.log(n)

分析:new Person() 返回 F,new p() 执行 F,this 指向新对象。

三、this 的高级用法与陷阱

1. call/apply/bind 的区别

  • call:参数列表分开传递。
  • apply:参数以数组形式传递。
  • bind:返回新函数,可延迟执行,可 new。

2. this 与箭头函数

箭头函数没有自己的 this,取外层作用域的 this。

3. this 与作用域链

this 不是作用域链的一部分,作用域链决定变量查找,this 决定函数执行时的上下文。

四、面试高频:手写 call/apply/bind/new

1. 手写 call

核心思路:把函数挂到 context 上执行,利用隐式绑定。

2. 手写 apply

和 call 类似,参数以数组形式传递。

3. 手写 bind

返回新函数,判断是否被 new 调用,原型链处理。

4. 手写 new

虽然本次代码未直接实现 new 的 polyfill,但理解 new 的底层机制很重要:

  1. 创建新对象。
  2. 将构造函数的 prototype 赋给新对象的 proto
  3. 执行构造函数,this 指向新对象。
  4. 返回新对象(如果构造函数返回对象则用返回值)。

五、综合案例:this 的灵活运用

1. 传参与 call 的妙用

javascript 复制代码
function identify() {
    return this.name.toUpperCase()
}
function speak() {
    var greeting = 'hello, I am ' + identify.call(this)
    console.log(greeting);
}
var me = { name: 'Ricardo' }
speak.call(me) // 输出 hello, I am RICARDO

2. bind 的构造函数特性

javascript 复制代码
function foo(x, y, z) {
    this.name = 'foo'
    console.log(this.a, x + y + z);
}
let obj = { a: 1 }
const baz = foo.bind(obj, 1, 2)
const p = new baz(4, 5)
console.log(p)
console.log(p.name)

分析:bind 返回的新函数被 new 调用,this 指向新对象,obj.a 未被继承。

六、面试真题与易错点

1. this 指向 window 还是 global?

浏览器环境下 var a = 1 会挂到 window 上,Node 环境下不会。

2. bind 实现的坑

  • 判断 new 调用时要用 instanceof。
  • 原型链要处理,否则 new 出来的对象原型不对。
  • 函数类型检查不能少。

3. apply 参数必须是数组

手写 apply 时要校验参数类型,否则会报错。

七、总结与面试技巧

  • this 的指向由调用方式决定,牢记四大规则。
  • call/apply/bind 是改变 this 的利器,手写实现要注意细节。
  • bind 返回的新函数可被 new 调用,原型链处理是难点。
  • 面试时多用代码举例,讲清原理,展示底层实现。
  • 遇到 this 不要慌,先看调用方式,再看代码环境。

祝你面试顺利,成为 JS this 机制的掌控者!🚀🎉


面试官最爱问的 this 题目

  1. 箭头函数的 this?

  2. setTimeout 里的 this?

  3. 构造函数和普通函数的 this 区别?

  4. 如何手写 bind 并支持 new?

  5. call/apply/bind 的底层原理?

相关推荐
xianxin_4 分钟前
CSS Outline(轮廓)
前端
moyu844 分钟前
遮罩层设计与实现指南
前端
Sammyyyyy10 分钟前
2025年,Javascript后端应该用 Bun、Node.js 还是 Deno?
开发语言·javascript·node.js
顾林海12 分钟前
Android MMKV 深度解析:原理、实践与源码剖析
android·面试·源码阅读
Java技术小馆12 分钟前
重构 Controller 的 7 个黄金法则
java·后端·面试
timeweaver19 分钟前
深度解析 Nginx 前端 location 配置与优先级:你真的用对了吗?
前端·nginx·前端工程化
鲸落落丶21 分钟前
网络通信---Axios
前端
wwy_frontend22 分钟前
React性能优化实战:从卡顿到丝滑的8个技巧
前端·react.js
小高00737 分钟前
面试官:npm run build 到底干了什么?从 package.json 到 dist 的 7 步拆解
前端·javascript·vue.js
天选打工圣体38 分钟前
个人学习笔记总结(四)抽离elpis并发布npm包
前端