this
官方定义:this是当前执行的环境。(下面好理解)
可以这样理解,在JavaScript中,this的指向是调用时决定的,而不是创建时决定的(敲黑板,作用域是创建时决定的)。谁调用了这个函数,那么这个函数中的this就指向谁。例如:obj.sayHi(),sayHi函数是被obj调用的,所以sayHi函数中的this就是obj,this.name其实就是访问在obj中定义的name。(我的言语组织能力就到这了。。)
全局上下文
在全局执行上下文中this都指代全局对象。this等价于window对象,var === this. === winodw.
console.log(window === this); // true
var a = 1;
this.b = 2;
window.c = 3;
console.log(a + b + c); // 6
函数上下文
在函数内部,this的值取决于 函数被调用 的方式。
function foo(){
return this;
}
console.log(foo() === window); // true
call、apply
this指向绑定到对象上。(直接调用函数)
改变this指向代码指向
var person = {
name: "axuebin",
age: 25
};
function say(job){
console.log(this.name+":"+this.age+" "+job);
}
say.call(person,"FE"); // axuebin:25
say.apply(person,["FE"]); // axuebin:25
可以看到,定义了一个say函数是用来输出name、age和job,其中本身没有name和age属性,我们将这个函数绑定到person这个对象上,输出了本属于person的属性,说明此时this是指向对象person的。call和apply的区别是第二个参数的传入格式不同。
bind
返回新的函数并将this指向绑定到bind的第一个参数。(返回一个新函数)
改变this指向代码演示
this.name="jack";
var demo={
name:"rose",
getName:function(){return this.name;}
}
console.log(demo.getName());//输出rose 这里的this指向demo
var another=demo.getName;
console.log(another())//输出jack 这里的this指向全局对象
//运用bind方法更改this指向
var another2=another.bind(demo);
console.log(another2());//输出rose 这里this指向了demo对象了
bind可以对一个函数预设初始参数。代码演示
预设初始参数的意思是:使用bind可以让返回的函数有默认的实参,如下代码中,1,2其实就是实参,后面不管在调用b方法时传入多少参数都不会影响1,2这两个实参。其实在就和 柯里化差不多。
function a() {
return Array.prototype.slice.call(arguments)
}
var b = a.bind(this,1,2)
console.log(b()) // [1,2]
var c = b(3,4,5)
console.log(c()) // [1,2,3,4,5]
拓展
用原生javascript 实现call
Function.prototype.myCall = function(context){
context = context || window;
context.fn = this;
var args = [];
if (arguments[1] === void 0) {
context.fn()
delete context.fn
return
}
for (var i=0; i<arguments.length; i++) {
args.push('arguments['+i+']')
}
eval('context.fn('+args+')')
delete context.fn
}
var name = '马士春'
var obj = {
name : 'msc',
sayHi: function() {
console.log(this.name, arguments)
}
}
obj.sayHi.myCall(window,3) // 输出: '马士春,[window,3]'
用原生javascript 实现apply (和call实现方法类似)
Function.prototype.myApply = function(context){
//如果第一个参数没有传或者为null、undefined,默认传入window
var context = context || window;
// this是执行的函数,并将其复制content的fn函数上。
context.fn = this;
// 获取第二个参数
var args = [];
// 如果没有传第二个参数,就直接执行
if ( arguments[1] === void 0) {
context.fn()
delete context.fn
return
} else if(Array.isArray(arguments[1])) { // 第二个参数是数组
// 这里的技巧很重要
for (var i=0; i<arguments[1].length; i++) {
args.push('arguments['+i+']')
}
//eval执行fn
eval('context.fn(' + args + ')')
} else { // 只接受数组或者不传为空,不能为其他类型
throw new Error ("CreateListFromArrayLike called on non-object");
}
delete context.fn
}
var name = '张三'
var obj = {
name: 'msc',
sayHi: function() {
console.log(this.name,Array.prototype.slice.call(arguments))
}
}
var bindObj = {
name: '马士春'
}
// 绑定到bindObj上,不传第二个参数
obj.sayHi.myApply(bindObj) // 输出'马士春,[]'说明this被绑定到了bindObj上了。
// 不传第一个参数,不传第二个参数
obj.sayHi.myApply() // 输出 '张三,[]' 说明不传第一个参数默认会绑定到window上
// 绑定到bindObj上,第二个参数传个数组
obj.sayHi.myApply(bindObj,[1,2]) 输出 '马士春,[1,2]'
用原生javascript 实现bind
//工具方法
function fnRandow(obj){
var randow = new Date().getTime()
if (obj.hasOwnProperty(randow)) {
fnRandow(obj)
} else {
return randow
}
}
//工具方法
function toArray(arg,start,end) {
var result = [];
start = start || 0;
end = end || arg.length;
for (var i=start; i< end; i++){
result.push(arg[i])
}
return result;
}
Function.prototype.myBind = function(context){
context = context || window;
var args = toArray(arguments,1),self = this;
var F = function() {}
F.prototype = this;
var bond = function() {
var resultArgs = toArray(arguments).concat(args)
return (function() {
//生成一随机数,并确保context对象中没有这个属性。
var FNrandom = fnRandow(context);
context[FNrandom] = self;
context[FNrandom](resultArgs)
delete context[FNrandom]
})()
}
bond.prototype = new F()
return bond
}
var obj = {
name: 'msc',
say: function(){
console.log(this.name, arguments)
}
}
var newo = {
name: '马士春'
}
var fn = obj.say.myBind(newo,1,2,3)
fn(4,5,6) // 输出 '马士春,[1,2,3,4,5,6]'