JS面试题--this指向+面试题

this其他补充

内置函数的绑定思考

image-20230109175317947

01_一些函数的this分析

// 1.setTimeout原理
// function hySetTimeout(fn, duration) {
    
    
//   fn.call(window)
// }

// hySetTimeout(function() {
    
    
//   console.log(this) // window
// }, 3000)

// setTimeout(function() {
    
    
//   console.log(this) // window
// }, 2000)

// 2.监听点击
// 这个只能调用一次onclick,再次调用会被覆盖
const boxDiv = document.querySelector(".box");
boxDiv.onclick = function () {
    
    
  console.log(this);
};
// 这个只能调用多次onclick,用会存储到一个数组里面
boxDiv.addEventListener("click", function () {
    
    
  console.log(this);
});
boxDiv.addEventListener("click", function () {
    
    
  console.log(this);
});
boxDiv.addEventListener("click", function () {
    
    
  // 原理 :fn。call(boxDiv)
  console.log(this);
});

// 3.数组.forEach/map/filter/find
// forEach的第一个参数是函数,第二个参数是this的绑定
var names = ["abc", "cba", "nba"];
names.forEach(function (item) {
    
    
  console.log(item, this);
}, "abc");
// map的第一个参数是函数,第二个参数是this的绑定
names.map(function (item) {
    
    
  console.log(item, this);
}, "cba");

规则优先级

image-20230109180611410

02_优先级-显示高于隐式绑定

var obj = {
    
    
  name: "obj",
  foo: function () {
    
    
    console.log(this);
  },
};

obj.foo();

// 1.call/apply的显示绑定高于隐式绑定
// obj.foo.apply('abc')
// obj.foo.call('abc')

// 2.bind的优先级高于隐式绑定
// var bar = obj.foo.bind("cba")
// bar()

// 3.更明显的比较
function foo() {
    
    
  console.log(this);
}

var obj = {
    
    
  name: "obj",
  foo: foo.bind("aaa"),
};

obj.foo(); //绑定显示绑定 [String: 'aaa']

03_优先级-new高于隐式绑定

var obj = {
    
    
  name: "obj",
  foo: function () {
    
    
    console.log(this);
  },
};

// new的优先级高于隐式绑定
var f = new obj.foo();

04_优先级-new高于显示绑定

// 结论: new关键字不能和apply/call一起来使用

// new的优先级高于bind
function foo() {
    
    
  console.log(this);
}

var bar = 

bar(); //aaa

var obj = new bar();

// new绑定 > 显示绑定(apply/call/bind) > 隐式绑定(obj.foo()) > 默认绑定(独立函数调用)

this规则之外 – 忽略显示绑定

image-20230109184526971

05_特殊绑定-忽略显示绑定

function foo() {
    
    
  console.log(this);
}

foo.apply("abc");
foo.apply({
    
    });

// apply/call/bind: 当传入null/undefined时, 自动将this绑定成全局对象
// foo.apply(null);
// foo.apply(undefined);

var bar = foo.bind(null);
bar();

this规则之外 - 间接函数引用

image-20230109184604420

06_特殊绑定-间接函数引用

// 争论: 代码规范 ;

var obj1 = {
  name: "obj1",
  foo: function () {
    console.log(this);
  },
};

var obj2 = {
  name: "obj2",
};

// obj2.bar = obj1.foo
// obj2.bar() //obj2
//  分号也是很关键的 ,不加分号会报错  会当成一行代码执行
(obj2.bar = obj1.foo)(); // 浏览器中是window  node中是global

0.测试代码
// 你不知道的JavaScript
function foo(el) {
  console.log(el, this.id);
}

var obj = {
  id: "awesome",
}[
  //  分号也是很关键的 ,不加分号会报错 -和obj{ id:'awesome'}[1,2,3].forEach(foo,obj)一起执行报错
  (1, 2, 3)
].forEach(foo, obj);
var nums = [1, 2, 3];
nums.forEach(foo, obj);


箭头函数 arrow function

image-20230109190252950

箭头函数的编写优化

image-20230109190349215

07_箭头函数的使用解析

// 1.编写箭头函数
// 1> (): 参数
// 2> =>: 箭头
// 3> {}: 函数的执行体
var foo = (num1, num2, num3) => {
    
    
  console.log(num1, num2, num3);
  var result = num1 + num2 + num3;
  console.log(result);
};

function bar(num1, num2, num3) {
    
    }

// 高阶函数在使用时, 也可以传入箭头函数
var nums = [10, 20, 45, 78];
nums.forEach((item, index, arr) => {
    
    });

// 箭头函数有一些常见的简写:
// 简写一: 如果参数只有一个, ()可以省略
nums.forEach((item) => {
    
    
  console.log(item);
});

// 简写二: 如果函数执行体只有一行代码, 那么{}也可以省略
// 强调: 并且它会默认将这行代码的执行结果作为返回值
nums.forEach((item) => console.log(item));
var newNums = nums.filter((item) => item % 2 === 0);
console.log(newNums);

// filter/map/reduce
var result = nums
  .filter((item) => item % 2 === 0)
  .map((item) => item * 100)
  .reduce((preValue, item) => preValue + item);
console.log(result);

// 简写三: 如果一个箭头函数, 只有一行代码, 并且返回一个对象, 这个时候如何编写简写
// var bar = () => {
    
    
//   return { name: "why", age: 18 }
// }
// 对象形式的简写,是个整体,告诉浏览器不是逻辑代码块而是一个对象
var bar = () => ({
    
     name: "why", age: 18 });

this规则之外 – ES6箭头函数

image-20230109192938327

image-20230109193002221

08_箭头函数的this获取

// 1.测试箭头函数中this指向
// var name = "why"

var foo = () => {
    
    
  console.log(this);
};
// 箭头函数没有this,唯一确定this的方式是从上级作用域中寻找
foo();
var obj = {
    
     foo: foo };
obj.foo();
foo.call("abc");

// 2.应用场景
var obj = {
    
    
  data: [],
  getData: function () {
    
    
    // 发送网络请求, 将结果放到上面data属性中
    // 在箭头函数之前的解决方案
    // var _this = this
    // setTimeout(function() {
    
    
    //   var result = ["abc", "cba", "nba"]
    //   _this.data = result
    // }, 2000);
    // 箭头函数之后 this的指向在上级作用域中寻找,这里面的this就是指向getDate函数,因为getDate函数是setTimeout的上级作用域
    // getData是普通函数 又因为obj.getData()通过隐式绑定 绑定到obj,所以这里面的this就变成了obj对象,就可以调用date了
    setTimeout(() => {
    
    
      var result = ["abc", "cba", "nba"];
      this.data = result;
      console.log(this);
    }, 2000);
  },
};

obj.getData();

3.如果getData也是一个箭头函数,那么setTimeout中的回调函数中的this指向window {
    
    }
// 1.测试箭头函数中this指向
// var name = "why"

var foo = () => {
    
    
  console.log(22, this);

  var boo = () => {
    
    
    console.log(11, this);
  };
  boo();
};
// 箭头函数没有this,唯一确定this的方式是从上级作用域中寻找
foo();

// 2.应用场景
var obj = {
    
    
  data: [],
  aaa: [],
  getData: () => {
    
    
    console.log(this);
    // 发送网络请求, 将结果放到上面data属性中
    // 在箭头函数之前的解决方案
    // var _this = this
    // setTimeout(function() {
    
    
    //   var result = ["abc", "cba", "nba"]
    //   _this.data = result
    // }, 2000);
    // 箭头函数之后 this的指向在上级作用域中寻找,这里面的this就是指向getDate函数,因为getDate函数是setTimeout的上级作用域
    // getData是箭头函数 obj.getData()通过隐式绑定 不起效果,所以这里面的this依然是window node中是{}
    setTimeout(() => {
    
    
      var result = ["abc", "cba", "nba"];
        //这里的this是最上层的作用域window node中是{}
      this.data = result;
      console.log(this);
    }, 2000);
  },
};

obj.getData();


面试题一:

image-20230109221055341

var name = "window";

var person = {
    
    
  name: "person",
  sayName: function () {
    
    
    console.log(this.name);
  },
};

function sayName() {
    
    
  var sss = person.sayName;
  sss(); // window: 独立函数调用
  person.sayName(); // person: 隐式调用
  person.sayName(); // person: 隐式调用  (person.sayName)()其实相当于就是person.sayName();
  (b = person.sayName)(); // window: 赋值表达式(独立函数调用)只要是赋值表达式--就是独立函数的调用---开发中不会写这种代码
}

sayName();

面试题二:

image-20230109221112349

var name = "window";

var person1 = {
    
    
  name: "person1",
  foo1: function () {
    
    
    console.log(this.name);
  },
  foo2: () => console.log(this.name),
  foo3: function () {
    
    
    return function () {
    
    
      console.log(this.name);
    };
  },
  foo4: function () {
    
    
    return () => {
    
    
      console.log(this.name);
    };
  },
};

var person2 = {
    
     name: "person2" };

// person1.foo1(); // person1(隐式绑定)
// person1.foo1.call(person2); // person2(显示绑定优先级大于隐式绑定)

// person1.foo2(); // window(箭头函数没有this,不绑定作用域,上层作用域是全局)  node中是undefined
// person1.foo2.call(person2); // window  node中是undefined

// person1.foo3()(); // window(独立函数调用)  person1.foo3()是个结果没有调用主体
// person1.foo3.call(person2)(); // window(独立函数调用)--重点是只看最终的this绑定是谁,这里是独立函数的调用
// person1.foo3().call(person2); // person2(最终调用返回函数式, 使用的是显示绑定)

// person1.foo4()(); // person1(箭头函数不绑定this, 上层作用域this是person1)
// person1.foo4.call(person2)(); // person2(上层作用域被显示的绑定了一个person2)
// person1.foo4().call(person2); // person1(上层找到person1)person1.foo4()使上层作用域变成person1,又因为箭头函数没有this,不绑定call,而是从上层作用域中找

面试题三:

image-20230109221125149

var name = "window";

function Person(name) {
    
    
  this.name = name;
  (this.foo1 = function () {
    
    
    console.log(this.name);
  }),
    (this.foo2 = () => console.log(this.name)),
    (this.foo3 = function () {
    
    
      return function () {
    
    
        console.log(this.name);
      };
    }),
    (this.foo4 = function () {
    
    
      return () => {
    
    
        console.log(this.name);
      };
    });
}
// 函数每次new的时候都会创建一个新对象 每次的传入的this也是根据自己传入的参数自定义的this
var person1 = new Person("person1");
var person2 = new Person("person2");

person1.foo1(); // person1
person1.foo1.call(person2); // person2(显示高于隐式绑定)

person1.foo2(); // person1 (上层作用域中的this是person1)
person1.foo2.call(person2); // person1 (上层作用域中的this是person1)

person1.foo3()(); // window(独立函数调用)
person1.foo3.call(person2)(); // window
person1.foo3().call(person2); // person2

person1.foo4()(); // person1
person1.foo4.call(person2)(); // person2
person1.foo4().call(person2); // person1

var obj = {
    
    
  name: "obj",
  foo: function () {
    
    },
};

面试题四:

image-20230109221137715

var name = "window";

function Person(name) {
    
    
  this.name = name;
  this.obj = {
    
    
    name: "obj",
    foo1: function () {
    
    
      return function () {
    
    
        console.log(this.name);
      };
    },
    foo2: function () {
    
    
      return () => {
    
    
        console.log(this.name);
      };
    },
  };
}

var person1 = new Person("person1");
var person2 = new Person("person2");

person1.obj.foo1()(); // window 独立函数的调用
person1.obj.foo1.call(person2)(); // window  person1.obj.foo1.call(person2)是改变foo1的this指向为person2 但是foo1返回值为一个独立函数 独立函数的调用为window
person1.obj.foo1().call(person2); // person2  隐式绑定person2

person1.obj.foo2()(); // obj 上层作用域   主要看的返回值这个函数的上层作用域foo2是被谁调取的  这里是obj 调取的
person1.obj.foo2.call(person2)(); // person2  上层作用域   主要看的返回值这个函数的上层作用域foo2通过call改变this成为person2 所以是person2
person1.obj.foo2().call(person2); // obj 上层作用域是foo2通过隐式绑定变成obj, 箭头函数没有this 看上层作用域的指向

//

// 上层作用域的理解
// var obj = {
    
    
//   name: "obj",
//   foo: function() {
    
    
//      上层作用域是全局
//   }
// }

// function Student() {
    
    
//   this.foo = function() {
    
    
// 上层作用域是function Student
//   }
// }

猜你喜欢

转载自blog.csdn.net/SOLar7SysteM/article/details/128622141