this的取值是什么,是在函数执行的时候确认的,不是函数定义的时候确认的!!!
this执行主体,谁把它执行的(和在哪创建&在哪执行都没有必然的关系)
this的五种使用情况分析
一、在普通函数中执行
函数在全局执行中的this指向Window,(在严格模式下是undefined)
function fn1() {
console.log(this);
}
fn1(); //输出Window
二、作为对象方法被调用
函数在对象中作为对象方法被调用中this指向该对象本身
function fn1() {
console.log(this);
}
const OBJ = {
name: 'OBJ',
fn1: fn1
}
OBJ.fn1(); // 输出对象:OBJ
三、作为元素的某个事件行为的绑定函数,当事件行为触发时
给当前元素的某个事件行为绑定函数,当事件行为触发,函数中的this是当前元素本身(attachEvent()中的this是Window)
document.body.addEventListener('click', function () {
console.log(this);
});
//点击浏览器中的body
//输出<body>...</body>
四、构造函数体中的this
构造函数体中的this指向的是当前类的实例
//案例一:
function Fn() {
this.name = '构造函数';
this.age = 10;
console.log(this);
}
let f = new Fn;
//案例二:
Array.prototype.log = function () {
console.log(this);
}
let a = [1,2,3];
a.log()
案例一输出:
案例二输出:
五、箭头函数中的this
箭头函数并没有属于自己的this,它所谓的this是捕获其所在上级作用域中的this,作为自己的 this 值,并且由于没有属于自己的this,所以是不会被new调用的(箭头函数无法new),这个所谓的this也不会被改变。
function fn1() {
console.log(this);//输出OBj
setTimeout(function () {
console.log(this);//因为在setTimeout()中,输出Window
},1000)
setTimeout(() => {
//箭头函数没有自己的this,所以其this指向的是上级作用域中的this:OBJ
console.log(this);
},2000)
}
const OBJ = {
name: 'OBJ',
fn1: fn1
}
OBJ.fn1();
输出:
六、通过 Function.prototype 上的 call / apply / bind 改变的this
通过基于Function.prototype上的call/apply/bind可以去改变this指向
function func(x, y) {
console.log(this, x, y);
}
let OBJ = {
name: 'OBJ'
};
func(1, 2); //输出Window 1 2
func.call(OBJ, 10, 20); //输出OBJ 10 20
func.apply(OBJ, [10, 20]); //输出OBJ 10 20
func.bind(OBJ, 10, 20)(); //输出OBJ 10 20
call的实现过程及其原理
func函数基于__proto__找到Function.prototype.call,把call方法执行
在call方法内部「call执行的时候」 call(context,...params)
1、 把func中的this改为obj
2、 并且把params接收的值当做实参传递给func函数
3、 并且让func函数立即执行
手写call源码:
Function.prototype.call = function call(context, ...params) {
let self = this,
key = Symbol('KEY'),
result;
// 检测context是否为null或undefined
context == null ? context = window : context;
//检验context是否为对象
/^(object|function)$/i.test(typeof context) ? null : context = Object(context);
context[key] = self;
result = context[key](...params);
delete context[key];
return result;
}
apply的实现过程及其原理
和上文的call类似,只是传参的时候需要传一个数组:apply(context,params(Array))
手写apply源码:
Function.prototype.call = function call(context, params) {
let self = this,
key = Symbol('KEY'),
result;
// 检测context是否为null或undefined
context == null ? context = window : context;
//检验context是否为对象
/^(object|function)$/i.test(typeof context) ? null : context = Object(context);
context[key] = self;
result = context[key](...params);
delete context[key];
return result;
}
bind的实现过程及其原理
func函数基于__proto__找到Function.prototype.bind,把bind方法执行
1、 和call/apply的区别:并没有把func立即执行
2、 把传递进来的OBJ/10/20等信息存储起来「闭包」
3、 执行bind返回一个新的函数
手写bind源码:
Function.prototype.bind = function bind(context, ...params) {
let self = this;
// 检测context是否为null或undefined
context == null ? context = window : context;
//检验context是否为对象
/^(object|function)$/i.test(typeof context) ? null : context = Object(context);
return function () {
self.call(context, ...params);
}
}