let f1 = new Function("x", "y", "return x + y");
function f2 (){};
let f3 = function() {};
let f4 = (x, y) => x + y;
this/arguments/new
this/arguments/new
this
就是该函数声明所在的环境(上下文)new Func
:构造函数体中的THIS是当前类的实例
// 事件绑定 DOM0 DOM2
let body = document.body;
body.onclick = function () {
// 事件触发,方法执行,方法中的THIS是BODY
console.log(this);
};
body.addEventListener('click', function () {
console.log(this); //=>BODY
});
// IE6~8中的DOM2事件绑定
box.attachEvent('onclick', function () {
console.log(this); //=>WINDOW
});
前面没有执行主体,this就默认指向window
(function () {
console.log(this); //=>window
})();
let obj = {
fn: (function () {
console.log(this); //=>window
return function () { }
})() //把自执行函数执行的返回值赋值给OBJ.FN
};
let obj = {
fn: (function () {
// 自执行函数 this => window
return function () {
console.log(this);
}
})()
};
obj.fn(); // obj
let fn = obj.fn;
fn(); // window
原型链中的this
[].slice(); //=>数组实例基于原型链机制,找到ARRAY原型上的SLICE方法([].slice),然后再把SLICE方法执行,此时SLICE方法中的THIS是当前的空数组
Array.prototype.slice(); //=>SLICE方法执行中的THIS:Array.prototype
[].__proto__.slice(); //=>SLICE方法执行中的THIS:[].__proto__===Array.prototype
构造函数体中的THIS在“构造函数执行”的模式下,是当前类的一个实例,并且THIS.XXX=XXX是给当前实例设置的私有属性
function Func() {
this.name = "F";
console.log(this);
}
Func.prototype.getNum = function getNum() {
// 而原型上的方法中的THIS不一定都是实例,主要看执行的时候,“点”前面的内容
console.log(this);
};
let f = new Func;
f.getNum();
f.__proto__.getNum();
Func.prototype.getNum();
不建议乱用箭头函数(部分需求用箭头函数还是很方法便的)
let obj = {
func: function () {
console.log(this);
},
sum: () => {
console.log(this);
}
};
obj.func(); //=>THIS:OBJ
obj.sum(); //=>THIS是所在上下文(EC(G))中的THIS:WINDOW
obj.sum.call(obj); //=>箭头函数是没有THIS,所以哪怕强制改也没用 THIS:WINDOW
回调函数中的THIS一般都是WINDOW(但是有特殊情况)
解决方法
1、用于保存this的变量_this
let obj = {
i: 0,
// func:function(){}
func() {
// THIS:OBJ
let _this = this;
setTimeout(function () {
// THIS:WINDOW 回调函数中的THIS一般都是WINDOW(但是有特殊情况)
_this.i++;
console.log(_this);
}, 1000);
}
};
obj.func();
2、基于bind把函数中的this预先处理
let obj = {
i: 0,
func() {
setTimeout(function () {
// 基于BIND把函数中的THIS预先处理为OBJ
this.i++;
console.log(this);
}.bind(this), 1000);
}
};
obj.func();
3、箭头函数中没有自己的THIS,用的THIS是上下文中的THIS,也就是obj
let obj = {
i: 0,
func() {
setTimeout(() => {
this.i++;
console.log(this);
}, 1000);
}
};
obj.func();
js会自动给你传入this
fn(1, 2) === fn.call(undefined, 1, 2);
obj.method("hi") === obj.method.call(obj, "hi");
arr[0](1, 2); === arr[0].call(arr, 1, 2)
例如
let fn = function(x, y){console.log(this)};
fn(1, 2)
let obj = {
fn
}
obj.fn(1, 2) //
let arr = [fn, 2];
arr[0](1,2);
let res = fn.call(obj, 10, 20);
console.log(res);
类数组操作
var arr = [];
for (var i = 0; i < arguments.length; i++) {
arr.push(arguments[i]);
}
return arr;
让slice方法中的this变为arguments
[].slice.call(arguments)
Array.prototype.slice.call(arguments)
let utils = (function () {
function toArray() {
return [].slice.call(arguments);
}
return {
toArray
};
})();
let ary = utils.toArray(10, 20, 30); //=>[10,20,30]
console.log(ary);
ary = utils.toArray('A', 10, 20, 30); //=>['A',10,20,30]
console.log(ary);
使用forEach
[].forEach.call(arguments, item => {})
如何让fn中的this变为obj => obj.fn() => 需要保证fn函数作为obj某个成员的属性值
思路:把函数作为要改变的THIS对象的一个成员,然后基于对象的成员访问执行函数即可obj.fn=fn; obj.fn();
Function.prototype.call = function call(context, ...params) {
context = context == null ? window : context;
let result;
// 把函数作为对象的某个成员值
context["fn"] = this;
// 基于 对象[成员]() 方式把函数执行,此时函数中的this就是对象,(把参数传递给函数,并且接收返回值)
result = context["fn"](...params);
// 设置的成员用完后删掉
delete context["fn"];
// 把函数的返回值作为call方法执行的结果返回
return result;
}
Function.prototype.call = function call(context, ...params) {
context = context == null ? window : context;
// 必须要保证CONTEXT得是一个对象
let contextType = typeof context;
if (!/^(object|function)$/i.test(contextType)) {
// context.constructor:当前值所属的类
// context = new context.constructor(context); //=>不适合Symbol/BigInt
context = Object(context);
}
let result;
let key = Symbol('KEY');
// 把函数作为对象的某个成员值(成员名唯一:防止修改原始对象的结构值)
context[key] = this;
// 基于“对象[成员]()”方式把函数执行,此时函数中的THIS就是对象(把参数传递给函数,并且接收返回值)
result = context[key](...params);
// 设置的成员用完后删除掉
delete context[key];
// 把函数的返回值作为CALL方法执行的结果返回
return result;
};
var name = 'lc';
function A(x, y) {
var res = x + y;
console.log(res, this.name);
}
function B(x, y) {
var res = x - y;
console.log(res, this.name);
}
B.call(A, 40, 30); // 10 "A"
B.call.call.call(A, 20, 10); // => A.call(20, 10) => NaN undefined
Function.prototype.call(A, 60, 50); // 无任何输出
Function.prototype.call.call.call(A, 80, 70); // NaN undefined
根据call源码拆分每一步
B.call(A,40,30); // 10 "A"
B.call.call.call(A, 20, 10); // NaN undefined
Function.prototype.call(A, 60, 50);
Function.prototype.call.call.call(A, 80, 70);
同第二步
想要改变点击事件的this
function func(x, y, ev) {
console.log(this, x, y, ev);
}
const obj = {
name: "test"
};
document.body.onclick = func; //=>this:body x:MouseEvent y:undefined
document.body.onclick = func.call(obj, 10, 20);
这样处理不行,事件绑定,绑定的是一个方法,此处是先把func执行(做了一些处理),把方法执行的返回结果赋值给事件绑定
解决方法:包一层匿名函数先执行call
document.body.onclick = function anonymous(ev) {
func.call(obj, 10, 20, ev);
};
和直接用bind是一样的效果
document.body.onclick = func.bind(obj, 10, 20);
function func(x, y, ev) {
console.log(this, x, y, ev);
}
const obj = {
name: "test"
};
Function.prototype.bind = function bind(context, ...params) {
// this -> 处理的函数 func
// context -> 要改变的函数中的THIS指向 obj
// params -> 最后给函数传递的实参 [10,20]
// 保存func,便于匿名函数中执行,也可以使用箭头函数,就不需要保存this了
let _this = this;
// 返回一个匿名函数
return function /* anonymous */(...args) {
// args -> 可能传递的事件对象等信息 [MouseEvent]
// this -> 匿名函数中的THIS是由当初绑定的位置触发决定的(总之不是func要处理的函数),所以需要保存_this
_this.call(context, ...params.concat(args))
}
}
document.body.onclick = func.bind(obj, 10, 20)
使用排序
console.log(arr.sort((a, b) => b - a)[0])
利用Math.max
Math.max 需要一项项传入数值进行比较
let arr = [4, 6, 10, 3, 5];
console.log(Math.max.apply(Math, arr))
var num = 10;
var obj = {
num: 20
};
obj.fn = (function (num) {
this.num = num * 3;
num++;
return function (n) {
this.num += n;
num++;
console.log(num);
}
})(obj.num);
var fn = obj.fn;
fn(5);
obj.fn(10);
console.log(num, obj.num);
以下代码的this是什么
button.onclick = function(e) {
console.log(this)
}
回答:不知道
因为this是call
的第一个参数,这里并未执行这个事件,所以未传入this
标准回答:
这个this是不确定的,需要看它如何调用
如果点击的是button的时候,浏览器会将button作为this传进来
如果通过其它方式调用就要看传入的this是什么了
let button = document.createElement('button');
button.innerHTML = "button";
document.querySelector('body').appendChild(button);
button.onclick = function(e) { // 点击指向button
console.log(this)
}
let fn = button.onclick;
fn(); // 不传this自调用默认指向window
fn.call({name: "LC"}) // 自调用传入指定this
注意点
let
声明的变量不会挂在window
上call
来尝试改写调用函数let length = 10
function fn(){console.log(this.length)}
let obj = {
length: 5,
method(fn){
fn()
arguments[0]()
}
}
obj.method(fn, 1)
调用的第一个fn()
的this就是指向window
,window.length
let obj = {
length: 5,
method(fn){
fn.call(window)
}
}
第二个调用中this指向的是arguments
,输出arguments
的长度
arguments[0].call(arguments)
let length = 10
function fn(){console.log(this.length)}
let obj = {
length: 5,
method(fn){
arguments[0].call(arguments) // 4
}
}
obj.method(fn, 1, 2, 3)