1、说一下this关键字的指向
1、全局作用域或普通函数调用:
当在全局作用域或通过普通函数调用使用 this
时,它通常指向全局对象(在浏览器中为 window
,在 Node.js 中为 global
)。
function globalFunction() {
console.log(this); // 在浏览器中指向 window,Node.js 中指向 global
}
globalFunction();
2、方法调用:
当在对象的方法中使用 this
时,它通常指向调用该方法的对象。
const obj = {
foo: function () {
console.log(this); // 指向 obj
},
};
obj.foo();
3、构造函数调用:
当在构造函数中使用 this
时,它指向新创建的实例对象。
function Person(name) {
this.name = name;
}
const person = new Person('John');
console.log(person.name); // 输出 "John"
4、显式绑定:
当使用 call
、apply
或 bind
方法显式设置函数调用的上下文时,this
将指向传递给这些方法的第一个参数。
function greet() {
console.log(`Hello, ${this.name}!`);
}
const alice = { name: 'Alice' };
const bob = { name: 'Bob' };
greet.call(alice); // 输出 "Hello, Alice!"
greet.call(bob); // 输出 "Hello, Bob!"
5、箭头函数:
箭头函数没有自己的 this
值。在箭头函数中使用 this
时,它会从包含箭头函数的最近的非箭头函数作用域中继承 this
值。
const obj = {
foo: function () {
const bar = () => {
console.log(this); // 指向 obj,因为箭头函数继承了最近的非箭头函数作用域的 this 值
};
bar();
},
};
obj.foo();
6、事件处理程序:
在 DOM 事件处理程序中,this
通常指向触发事件的元素。例如,当用户点击一个按钮时,this
将指向被点击的按钮。
<button id="myButton">Click me</button>
<script> document.getElementById('myButton').addEventListener('click', function () {
console.log(this); // 指向被点击的按钮元素
});
</script>
7、类方法:
在 ES6 类的方法中使用 this
时,它指向调用该方法的实例。这与普通对象方法的行为类似。
class Person {
constructor(name) {
this.name = name;
}
greet() {
console.log(`Hello, ${this.name}!`);
}
}
const alice = new Person('Alice');
alice.greet(); // 输出 "Hello, Alice!"
8、使用 setTimeout
或 setInterval
:
在 setTimeout
或 setInterval
的回调函数中使用 this
时,它通常指向全局对象
function MyObject() {
this.value = 'Hello';
setTimeout(function () {
console.log(this.value); // undefined,因为此时的 this 指向全局对象
}, 1000);
}
const obj = new MyObject();
2、判断数组的方法以及优缺点?
1、instanceof
操作符
const arr = [];
console.log(arr instanceof Array); // 输出 true
优点:
- 简单易用,可读性好。
缺点:
- 在涉及多个全局执行上下文(例如来自不同
iframe
的对象)时,可能会失效。这是因为每个全局执行上下文都有其自己的Array
构造函数,instanceof
会检查对象是否是当前执行上下文中Array
的实例。
2、 Array.isArray()
方法(推荐)
const arr = [];
console.log(Array.isArray(arr)); // 输出 true
优点:
- 可靠性更高,能正确处理多个全局执行上下文的情况。
- 简单易用,可读性好。
缺点:
- 在 ECMAScript 5 之前的浏览器(如 IE8 及更早版本)中不可用,需要进行 polyfill。
3、 使用 Object.prototype.toString.call()
方法
const arr = [];
console.log(Object.prototype.toString.call(arr) === '[object Array]'); // 输出 true
优点:
- 能在所有 JavaScript 环境中可靠地工作,包括旧的浏览器。
- 能正确处理多个全局执行上下文的情况。
缺点:
- 代码较长,可读性较差。
4、使用 constructor
属性
const arr = [];
console.log(arr.constructor === Array); // 输出 true
优点:
- 简单易用,可读性好。
缺点:
- 如果某个对象的原型链中
constructor
属性被篡改,此方法可能失效。 - 在涉及多个全局执行上下文的情况下,可能会失效。
5、 使用 Array.prototype.isPrototypeOf()
方法(不推荐)
const arr = [];
console.log(Array.prototype.isPrototypeOf(arr)); // 输出 true
使用 Array.prototype.isPrototypeOf()
方法判断一个变量是否为数组的优缺点如下:
优点:
- 可以在多个全局执行上下文(例如不同的
iframe
)中正常工作,因为它仅检查对象的原型链是否包含Array.prototype
。
缺点:
- 不如其他方法简洁明了,可读性较差。
- 在某些情况下,例如当数组的原型链被篡改时,此方法可能会产生错误的结果。
3、什么是函数柯里化?
柯里化是将一个接受多个参数的函数转换为一系列接受单一参数的函数的过程。换句话说,柯里化是把一个多参数函数转化为一连串的一元函数(每个函数只接受一个参数)。
假设我们有一个普通的多参数函数 add
:
function add(x, y) {
return x + y;
}
console.log(add(1, 2)); // 输出 3
现在,我们将 add
函数柯里化:
function curryAdd(x) {
return function(y) {
return x + y;
};
}
const add1 = curryAdd(1); // 返回一个新函数,该函数将 1 作为第一个参数
console.log(add1(2)); // 输出 3
在上述示例中,我们将 add
函数转换为柯里化版本的 curryAdd
函数。curryAdd
函数接受一个参数 x
,并返回一个新函数,该新函数接受另一个参数 y
。调用这个新函数时,它将返回 x
和 y
的和。
4、setInterval时间为什么不准确?怎么解决?
setInterval时间不准确的原因是,它的回调函数并不是到时后立即执行,而是等系统计算资源空闲下来后才会执行。而下一次触发时间则是在setInterval回调函数执行完毕之后才开始计时。所以如果setInterval内执行的计算过于耗时,或者有其他耗时任务在执行,setInterval的计时会越来越不准,延迟很厉害。
解决的方法是,使用setTimeout代替setInterval,并且每次执行结束后,根据误差值动态调整下一次执行的间隔时间。