this总是一个对象,是属性或方法“当前”所在的对象。this的设计目的是在函数体内部,指代函数当前的运行环境。
1、this的使用场合
1.1 全局环境
正常模式中,全局环境使用this,它指的就是顶层对象window(函数内/外部均指向window)。
注:本文在浏览器中测试,默认顶层对象是window
// 正常模式
this === window // true
function f () {
return this;
}
f() === window // true
严格模式中,全局环境使用this,函数外部指向window;函数内部,this 是 undefined(严格模式中,禁止 this 关键字指向全局对象)。
// 严格模式
'use strict'
this === window // true
function f () {
return this;
}
f() === undefined // true
严格模式相关,本文不展开说明。
1.2 构造函数
构造函数中的this,指的是实例对象。
function Person (name) {
this.name = name
}
let p = new Person('张三');
console.log(p.name) // "张三"
1.3 对象的方法
如果对象的方法里面包含this,this指向方法运行时所在的对象。该方法赋值给另一个对象,会改变this的指向。
1)基本用法,this指向调用它的对象
let obj ={
name: '张三',
print: function () {
console.log(this.name);
}
};
obj.print() // "张三" --> this 指向 obj
2)函数单独定义,并赋值给对象的方法(this指向可变,指向调用它的对象)
function print () {
console.log(this.fullName);
}
let A = {
fullName: '张三',
print
};
let B = {
fullName: '李四',
print
};
A.print(); // "张三" --> this 指向 A
B.print(); // "李四" --> this 指向 B
print(); // undefined --> this 指向 window
3)对象的方法赋给另一个对象(this指向可变,指向调用它的对象)
let A = {
fullName: '张三',
print: function () {
console.log(this.fullName);
}
};
let B = {
fullName: '李四'
};
B.print = A.print;
A.print(); // "张三" --> this 指向 A
B.print(); // "李四" --> this 指向 B
let f = A.print;
f(); // undefined --> this 指向 window
4)其他改变this指向的用法(this指向window的特殊用法)
let obj ={
foo: function () {
return this;
}
};
(obj.foo = obj.foo)() // Window
(false || obj.foo)() // Window
(true && obj.foo)() // Window
(1, obj.foo)() // Window
5)this 在多层对象内部的一个方法里(this 指向当前一层的对象,且不会继承更上面的层)--重点关注
let a = {
p: 'Hello',
b: {
d: 'world',
m: function() {
console.log(this.p);
console.log(this.d);
}
}
};
a.b.m(); // --> this 指向 a.b
// undefined // a.b.p
// "world" // a.b.d
上面的例子 实际执行的是下面的代码:
let b = {
d: 'world',
m: function() {
console.log(this.p);
console.log(this.d);
}
}
let a = {
p: 'Hello',
b
};
(a.b).m(); // 等同于 b.m()
6)方法中包含多层this(第一层this 指向当前一层的对象,第二层嵌套方法中的this指向window)
let obj ={
foo: function () {
console.log(this); // --> this 指向 obj
let bar = function () {
console.log(this); // --> this 指向 window
};
bar();
}
};
obj.foo();
// obj
// Window
上述实际执行的是下面的代码
let bar = function () {
console.log(this);
}
let obj ={
foo: function () {
console.log(this);
bar();
}
};
obj.foo();
// obj
// Window
让内层this指向外层对象的方法:
- 用一个变量固定this的值
let obj ={ foo: function () { console.log(this); // --> this 指向 obj const that = this; let bar = function () { console.log(that); // --> this 指向 obj }; bar(); } }; obj.foo(); // obj // obj
- Function.prototype.bind()
let obj ={ foo: function () { console.log(this); // --> this 指向 obj let bar = function () { console.log(this); // --> this 指向 obj }.bind(this); bar(); } }; obj.foo(); // obj // obj
- 箭头函数。箭头函数没有自己的this对象,内部的this就是定义时上层作用域中的this。箭头函数内部的this指向是固定的。--本节(对象的方法)中所有的例子都可以改成箭头函数的形式,看this的指向,请自行尝试
let obj ={ foo: function () { console.log(this); // --> this 指向 obj let bar = () => { console.log(this); // --> this 指向 obj }; bar(); } }; obj.foo(); // obj // obj
7)数组处理方法中的 this(this指向window)
let obj ={
v: 'hello',
p: [ 'a1', 'a2' ],
foo: function () {
this.p.forEach(function (item) {
console.log(this.v + ' ' + item); // --> this 指向 window
})
}
};
obj.foo();
// undefined a1
// undefined a2
让内层this指向外层对象的方法:
- 用一个变量固定this的值
- Function.prototype.bind()
- 箭头函数
- forEach接受第二个参数,绑定参数函数的this变量。
let obj ={ v: 'hello', p: [ 'a1', 'a2' ], foo: function () { this.p.forEach(function (item) { console.log(this.v + ' ' + item); // --> this 指向 window }, this); } }; obj.foo(); // hello a1 // hello a2
1.4 DOM 事件操作
1)HTML 的 on- 属性
<div id="btn" onclick="console.log('HTML的onclick属性', this);">点击</div>
// --> this 指向 当前DOM元素
<div id="btn" onclick="(function (){console.log(this);})()">点击</div>
// --> this 指向 window
<div id="btn" onclick="clickFun()">点击</div>
<script>
function clickFun() {
console.log('HTML的onclick属性', this); // --> this 指向 window
}
</script>
2)元素节点的事件属性
<div id="btn">点击</div>
<script>
let oDiv = document.getElementById('btn');
oDiv.onclick = function () {
console.log('元素节点的事件属性', this); // --> this 指向 当前DOM元素
}
</script>
3)EventTarget.addEventListener()
<div id="btn">点击</div>
<script>
let oDiv = document.getElementById('btn');
oDiv.addEventListener('click', function () {
// --> this 指向 当前DOM元素
console.log('DOM节点的addEventListener方法', this);
})
</script>
1.5 使用 window.setTimeout()
var id = 21;
function f1() {
setTimeout(function () {
console.log('id:', this.id);
}, 100)
}
function f2() {
setTimeout(() => {
console.log('id:', this.id);
}, 100)
}
// 普通函数,执行时this指向全局对象window
f1(); // id: 21
f1.call({id: 42}); // id: 21
// 箭头函数,this总是指向函数定义生效时所在的对象
f2(); // id: 21
f2.call({id: 42}); // id: 42
2、绑定 this 的方法
JS提供了call、apply、bind三个方法,下文详述。
2.1 Function.prototype.call()
1)定义
call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。
语法:function.call(thisArg, arg1, arg2, ...)
其中:
thisArg:可选。在 function 函数运行时使用的 this 值。
thisArg应该是一个对象,如果是空、null和undefined,则默认传入全局对象window;
原始值会被包装。
var n = 123;
var obj = { n:456 };
function f () {
console.log(this.n);
}
f.call(); // 123
f.call(1); // undefined
f.call(1n); // undefined
f.call('1'); // undefined
f.call(null); // 123
f.call(undefined); // 123
f.call(Symbol()); // undefined
f.call([]); // undefined
f.call(obj); // 456
2)手写call,参考答案(仅支持浏览器环境)
Function.prototype.myCall = function (context, ...args) {
if (context === null || context === undefined) {
context = window
} else {
context = Object(context)
}
const fn = Symbol('fn')
context[fn] = this;
const result = context[fn](...args);
delete context[fn];
return result;
}
// 验证
var n = 123;
var obj = { n: 456 };
function f() {
console.log(this.n);
}
f.myCall(); // 123
f.myCall(null); // 123
f.myCall(obj); // 456
f.myCall(1); // undefined
2、Function.prototype.apply()
apply方法的作用与call方法类似。唯一的区别是,它接收一个数组(或类数组对象)作为函数执行时的参数。
语法:function.apply(thisArg, argsArray)
apply的一些应用
1)将数组各项添加到另一个数组
const arr = ['a', 'b'];
const elements = [0, 1, 2];
arr.push.apply(arr, elements);
console.log(arr); // [ 'a', 'b', 0, 1, 2 ]
2)Math.max/Math.min 求得数组中的最大/小值
const numbers = [5, 6, 2, 3, 7];
// 基本等同于 Math.max(numbers[0], ...) 或 Math.max(5, 6, ..)
Math.max.apply(null, numbers); // 7
Math.min.apply(null, numbers); // 2
3)将数组的空元素变为undefined。数组的map方法会跳过空元素,但是不会跳过undefined。
const arr = ['a',,'b']
function f (item) {
console.log(item);
};
arr.map(f);
// a
// b
Array.apply(null, arr).map(f);
// a
// undefined
// b
4)转换类似数组的对象
[].slice.apply({0: 'a', length: 1}) // ['a']
Array.prototype.slice.apply({0: 'a', length: 1}) // ['a']
[].slice.apply('abc') // ['a', 'b', 'c']
5)绑定回调函数的对象
var nm = 'wNm';
const obj = {
nm: 'oNm'
}
function f(cb) {
cb()
cb.apply(this);
cb.apply(obj);
}
function print() {
console.log(this.nm);
}
f(print)
// 'wNm'
// 'wNm'
// 'oNm'
3、Function.prototype.bind()
bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。
语法:function.bind(thisArg[, arg1[, arg2[, ...]]])
用法:
1)bind()创建绑定函数
var count = 100;
var counter = {
count: 0,
inc: function () {
this.count++;
}
};
let fun1 = counter.inc;
fun1(); // --> this 指向 window
count // 101
counter.count // 0
var func = counter.inc.bind(counter);
func(); // --> this 指向 counter
count // 101
counter.count // 1
2)bind() 使一个函数拥有预设的初始参数。
function addArguments(arg1, arg2) {
return arg1 + arg2
}
let addOne = addArguments.bind(null, 1/*arg1 = 1*/);
console.log(addOne(2/*arg2的值*/)); // 3
The end.
参考连接: