【你不知道的JS】–> 关于this的详解
–1--首先我们要思考一个问题,为什么要使用this?
function identify(){
return this.name.toUpperCase();
}
function speak(){
var greeting = "Hello,I'm" + identify.call(this);
console.log(greeting);
}
var me = {
name:"kyle"
}
console.log(identify.call(me)); //KYLE
speak(me); //Hello,I'm KYLe.
这段代码可以在不同的上下文对象(me)中重复使用两个函数,不用针对每个对象编写不同的函数。
如果不使用this的话,那就需要给identify()和speak()显示的传入一个上下文对象(context)。
function identify(context){
return this.name.toUpperCase();
}
function speak(context){
var greeting = "Hello,I'm" + identify.call(context);
console.log(greeting);
}
var me = {
name:"kyle"
}
console.log(identify.call(me)); //KYLE
speak(me); //Hello,I'm KYLe.
–2--作为一个初学者,我对this也有很浅的理解,很多时候把2this英语话的想当然当作函数本身了,那么我们一起来分析一下。
function foo(num){
console.log("foo:" + num);
this.count++;
}
foo.count = 0;
for(var i = 0;i < 10;i ++)
{
if(i > 7){
foo(i);
}
}
// foo:8
// foo:9
那么foo.count被调用了几次呢?
console.log(foo.count); //0
显然foo(…)被调用了两次,但是foo.count依旧是0,所以this指向自身的理解大错特错了。
(那么当增加的count和预期的不一样时,增加的是哪个count?实际上这段代码创建了一个全局变量count)
假如我放弃this,来使用词法作用域解决问题呢?
答案当然是ok的。
function foo(num){
console.log("foo:" + num);
date.count++;
}
var date = {
count:0
}
for(var i = 0;i < 10;i ++)
{
if(i > 7){
foo(i);
}
}
console.log(date.count); //2
还可以使用call()来解决问题:
function foo(num){
console.log("foo:" + num);
this.count++;
}
foo.count = 0;
for(var i = 0;i < 10;i ++)
{
if(i > 7){
foo.call(foo,i);
}
}
console.log(foo.count); //2
call在这里显示强制的将this指向改变从而达到目标效果。
–3--.那么this的指向到底怎么看?
我们需要了解一些this的绑定规则。
①:默认绑定:
function foo(){
console.log(this.a)
}
var a = 9;
foo(); // 9
因为在这里foo是直接使用不带任何修饰的函数进行调用的,因此只能使用默认绑定,不能使用其他规则。
在这里this直接指向全局变量a。
加入在严格模式下:
this将会绑定到undefined。
function foo(){
"use strict"
console.log(this.a)
}
var a = 9;
foo(); // a not undefined of foo
②隐式绑定
function foo(){
console.log(this.a)
}
var obj = {
a:2,
foo:foo
}
obj.foo(); //2
隐式绑定也会存在问题—即隐式绑定的函数会丢失绑定对象:
function foo(){
console.log(this.a)
}
var obj = {
a:2,
foo:foo
}
var bar = obj.foo;
var a = 3;
bar();
//bar 是对obj.foo的一个引用,但实际上它绑定的是foo函数本身。 默认绑定this -->全局的a.
③显示绑定(call(),apply())
同于①中的例子一样,显示绑定会强制改变this指向(也叫做硬绑定):
function foo(){
console.log(this.a)
}
var obj = {
a:2
}
var bar = function(){
foo.call(obj)
}
var a = 3;
bar(); // 2 硬绑定的bar不会再修改他的this
类如下面常用的一个封装函数,实现特定的功能:
function foo(something){
console.log( this.a, something);
return this.a + something;
}
var obj = {
a:2
}
var bar = function(){
return foo.apply( obj, arguments);
};
var b = bar(3); // 2 3
console.log(b); //5
重点来了:由于硬绑定是一种非常常用的模式,所以ES5提供了内置方法Function.prototype.bind,用法如下:
foo.__proto__ == Function.prototype //true
function foo(something){
console.log( this.a, something)
return this.a + something;
}
var obj = {
a:2
}
var bar = foo.bind(obj);
var b = bar(3); //2 3
console.log(b) //5
④new绑定
我们知道在使用构造函数的时候,使用new来调用函数,或者说发生构造函数调用时,会自动执行下面的操作。
1.常见或者是说构造一个全新的对象。
2.这个新对象会被执行【prototye】连接。
3.这个相信对象会绑定到函数调用的this。
4.这个函数如果没有其他返回对象,最终自动返回这个新对象。
看了这么多,到底是什么意思呢?
举个栗子:
function foo(a){
this.a = a;
}
var bar = new foo(2);
console.log(bar.a); // 2
发生的过程如下:
1.var obj = {};
2.obj.__proto__ == foo.prototype;
3.foo.call(bar); this指向改变, this --> bar
4.return obj
4.如何去判断this指向?
上述四种形式的绑定是有优先级顺序的,即new绑定>显示绑定>隐式绑定>默认绑定。(具体可参考(你所不知道的JS上卷p92)
那么根据绑定的优先级我们可以来确定this的指向:
1.函数是否在new中调用(new 绑定)? 如果是的话this绑定的是新创建的对象:
var bar = new foo() // this -- >bar
2.函数是否通过call()和apply()(显示绑定,或者硬绑定)调用?如果是的话,this绑定的是指定的对象。
var bar = foo.call(obj) //this --?obj
3.函数是否在上下文中调用(隐式绑定)?如果是的话,this绑定在那个上下文的对象上。
var bar = obj.foo() //this -->obj
4.如果都不是的话,使用默认绑定。在严格模式下绑定到undefined,否则绑定到全局对象上。
var bar = foo();
一些自己学习内容的总结。 不够深奥和详细请求见谅,哈哈哈。 2019-6-17