基础-必会知识(下)

3 基础-必会知识(下)

3.1 请问你了解js中的this绑定机制吗?

【考点映射】

  • this绑定机制

【频率】★★★★★(面试极有可能出代码题(面试官给代码,答输出))

【难度】☆☆

【参考答案】

this特点:

this是js的关键字之一,它是对象自动生成的一个内部对象,只能在对象内部使用

随着使用场合的不同,this的值会发生变化,并不是一成不变的

this指向完全取决于:什么地方以什么方式调用,而不是 创建时

this 4种绑定机制:默认绑定、隐式绑定、显示绑定、new绑定,箭头函数的this不适用于这4种绑定机制,需要单独分析,将会在后续ES6新特性章节中做重点分析

  • 默认绑定(函数调用时无任何调用前缀的情景)

没有其他绑定规则存在时的默认规则,也是函数调用中最常见情况
function fn1() {
    let fn2 = function () {
        console.log(this); //window
        fn3();
    };
    console.log(this); //window
    fn2();
};
function fn3() {
    console.log(this); //window
};
fn1();

代码中无论函数声明在哪,在哪调用,由于函数调用时前面并未指定任何对象,这种情况下this指向全局对象window

注意:在严格模式下(use strict),全局对象将无法使用默认绑定,会报undefined错误

function fn() {
    console.log(this); //window
    console.log(this.a);
};
function fn1() {
    "use strict";
    console.log(this); //undefined
    console.log(this.a);
};
var a = 1;
fn() //1
fn1() //TypeError: Cannot read property 'a' of undefined
  • 隐式绑定

在函数调用时,前面存在调用它的对象,即函数的调用是在该对象上触发的,调用位置上存在上下文对象,那this就会隐式绑定到该对象上

function foo() { 
    console.log(this.a);
}
var a = 2;
var obj = { 
    a: 3,
    foo: foo 
};
obj.foo(); //3

代码中foo函数被当做引用属性,被添加到obj对象上。调用过程:获取obj.foo属性→ 根据引用关系找到foo函数,执行调用

这里对foo的调用存在上下文对象obj,this进行了隐式绑定,即this绑定到了obj上,this.a被解析成了obj.a

多层调用链(面试高频考题)(函数调用前存在多个对象,this指向距离调用自己最近的对象)

function foo() { 
    console.log( this.a );
}
var a = 2;
var obj1 = { 
    a: 4,
    foo: foo 
};
var obj2 = { 
    a: 3,
    obj1: obj1
};
obj2.obj1.foo();  //4

代码中调用链不只一层,存在obj1、obj2两个对象,先获取obj2.obj1→通过引用获取到obj1对象,再访问 obj1.foo →最后执行foo函数调用,获取最后一层调用的上下文对象,即obj1,所以结果是4(obj1.a)

  • 隐式丢失

在一些特殊情况下,会存在隐式绑定丢失问题,最常见:参数传递、变量赋值

参数传递

var a = 1;
let obj = {
    a: 2,
    fn: function () {
        console.log(this.a);
    }
};
function fn1(param) {
    param();
};
fn1(obj.fn);//1

代码中将 obj.fn 作为参数传递进 fn1 中执行,只是单纯地传递了一个函数而已,this并没有跟函数绑在一起,发生了隐式丢失,this依旧指向window

变量赋值(本质上与传参相同)

var a = 1;
let obj = {
    a: 2,
    fn: function () {
        console.log(this.a);
    }
};
let fn1 = obj.fn;
fn1(); //1
  • 显式绑定(call、apply、bind)

let obj1 = {
    a: 2
};
let obj2 = {
    a: 3
};
let obj3 = {
    a: 4
}
var a = 1;

function fn() {
    console.log(this.a);
};
fn(); //1
fn.call(obj1); //2
fn.apply(obj2); //3
fn.bind(obj3)(); //4

代码中,我们分别通过call、apply、bind改变了函数fn的this指向

通常,js中调用一个函数时(函数调用),函数处于一个被动的状态,而call与apply让函数从被动变主动,函数能主动选择自己的上下文,这种写法又称之为函数应用

注意:若使用函数应用的方法改变this指向时,指向参数是null或者undefined,那么 this 将指向全局对象

  • new绑定

function Fn(){     
    this.a = 1; 
}; 
let echo = new Fn(); 
console.log(echo.a)  //1

代码中,构造函数调用创建了一个新对象echo,而在函数体内,this将指向新对象echo上(可以抽象理解为新对象就是this)

  • this绑定优先级

如果一个函数调用存在多种绑定方法,this最终指向是什么呢?this绑定优先级为:

显式绑定 > 隐式绑定 > 默认绑定

new绑定 > 隐式绑定 > 默认绑定

注意:不存在显式绑定和new绑定同时生效的场景,若同时写会直接报错

【延伸考点】

1、作用域链与原型链有什么区别?

作用域链:当访问一个变量时,首先在当前作用域查找标识符,如果没有找到就去父作用域找,作用域链顶端是全局对象window,如果window都没有这个变量则报错

原型链:当在对象上访问某属性时,首先会查找当前对象,如果没有就顺着原型链往上找,原型链顶端是null,如果全程都没找到则返回undefined,而不是报错


3.2 请问call、apply与bind有什么区别?

【考点映射】

  • call、apply与bind区别

【频率】★★★★

【难度】☆

【参考答案】

call、apply与bind:都能用于改变this绑定

apply与call:

都是函数应用,指定this的同时也会执行函数,参数传递方式不同

call与apply的绑定只适用当前调用,调用完毕即失效,下次要用还得重新绑

call:接受一个参数列表,第一个参数指向this,其余的参数在函数执行时都会作为函数形参传入函数

apply:除了第一个参数作为this指向外,其它参数都被包裹在一个数组中,在函数执行时同样会作为形参传入

let o = {
    a: 1
};
function fn(b, c) {
    console.log(this.a + b + c);
};
// fn.call(this, arg1, arg2, ...);
fn.call(o, 2, 3); // 6
// fn.apply(this, [arg1, arg2, ...]);
fn.apply(o, [2, 3]); // 6

bind:

绑定this后并不会立马执行,而是返回一个全新的boundFcuntion绑定函数

bind属于硬绑定,返回的boundFunction的this指向无法再次通过bind、apply或 call 修改

let o1 = {
    a: 1
};
let o2 = {
    a: 2
};
function fn(b, c) {
    console.log(t

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

前端岗面试求职真题解析 文章被收录于专栏

前端岗位面试求职攻略及真题解析~~

全部评论

相关推荐

评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务