js this指针绑定问题 及闭包小析

时间:2022-11-15 22:46:16

tips : 本博客示例部分引用至
http://developer.51cto.com/art/200907/136245.htm
http://coolshell.cn/articles/6731.html

js this 指针绑定

对于学过c++的同学,对象方法定义时,并不会显示定义this作为参数,但是在调用时,编译器会默认将this 传入本方法,而此时this指向的是当前实例【对象】

#include <iostream>
using namespace std;

class Person{

public string name;
private int age;

public Person(string name,int age) {
this.name = name;
this.age = age;
}

public void setAge(int age) {
this.age= age;
}

public void sayHellow(){
cout<< this.name << "say hellow to you" << endl;
}
};

python 类的定义,显示指定this参数,但调用类方法时,有编译器默认绑定this 指向当前实例

class people:  

name = ''
__age = Null

def __init__(self,name,age):
self.name = name
self.__age = age;

def speak(self):
print("%s is %d years" %(self.name,self.__age))

对于JS这种脚本语言也不例外,函数或者对象方法 被定义时,并不存在this变量,只有函数或对象方法被调用时,this作为参数隐性的传入,这样以来,当前谁调用该函数或对象方法 this指针就指向谁

var name = "Kevin Yang";  
function sayHi(){
alert("你好,我的名字叫" + this.name);
}
sayHi();

此时,sayHi()方法被执行,在js中所有变量,对象,函数都默认归属于window对象,
换句话说,执行sayHi()函数,其实是在执行window对象下sayHi()对象方法 标准写法 window.sayHi() ,只不过window一般可以省略
也就是说 执行sayHi()时,默认将当前对象window传入【将this绑定到window对象】

var name = "Kevin Yang";  
function sayHi(){
alert("你好,我的名字叫" + this.name);
}
var person = {};
person.sayHello = sayHi;
person.sayHello()

此时sayHi()函数引用, 被赋值给person对象的sayHellow属性,当执行sayHello时,当前对象是person,所以this执行person,但是person对象中没有name属性,所以undefined

function sayHi(){  
alert("当前点击的元素是" + this.tagName);
}
<input name="btnTest" type="button" value="点击我" onclick="sayHi()">

当按钮被点击时,实际上执行的是下面代码

document.getElementById("btnTest").onclick = function(){  
sayHi();
}

此时的sayHi()实际上还是 执行widowl.sayHi()所以 sayHi()函数中 this指向的是window对象
可以改进如下:

function sayHi(el){  
alert("当前点击的元素是" + el.tagName);
}

<input name="btnTest" type="button" value="点击我" onclick="sayHi(this)">

改进后执行过程如下:

document.getElementById("btnTest").onclick = function(){  
sayHi(this);
}

临时变量导致的this指针丢失

var Utility = {  
decode : function(str){
return unescape(str);
},
getCookie : function(key){
var value = "i%27m%20a%20cookie";
return this.decode(value);
}
};
alert(Utility.getCookie("identity"))
var getCookFunc = Utility.getCookie;
alert(getCookFunc("identity"))

当Utility对象下getCookie方法 赋值给全局变量 getCookFunc后,执行getCookFun()实际上是 window.getCookFun() ,当前对象是window,所以调用getCookFun时,window被默认传入【this指向window对象】


tips : 详细参加本博客参考文档材料,见博客尾部

闭包

闭包使得函数内容定义的局部变量可以被外部访问到
使得局部函数内部的局部变量在函数执行完成后可以继续存在
在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。

function greeting(name) {
var text = 'Hello ' + name; // local variable
// 每次调用时,产生闭包,并返回内部函数对象给调用者
return function() { alert(text); }
}
var sayHello=greeting("Closure");
sayHello() // 通过闭包访问到了局部变量text

当在一个循环中赋值函数时,这些函数将绑定同样的闭包

function buildList(list) {
var result = [];
for (var i = 0; i < list.length; i++) {
var item = 'item' + list[i];
result.push( function() {alert(item + ' ' + list[i])} );
}
return result;
}

function testList() {
var fnlist = buildList([1,2,3]);
// using j only to help prevent confusion - could use i
for (var j = 0; j < fnlist.length; j++) {
fnlist[j]();
}
}

buildList()函数执行完成后,buildList函数内容定义的局部变量item , i 并没有销毁,因为buildList函数内部定义了匿名函数,并将匿名函数引用压入result列表中,最后返回result列表,赋值给testList函数内部定义的局部变量 fnlist ,
匿名函数【闭包】依赖父函数对象【buildList】,所以只要匿名函数引用还存在,垃圾回收机制就不会销毁buildList函数对象,buildList函数内部定义的局部变量也就依然存在于内存中。
当buildList函数内for循环执行完成后,i=4, item=’item’ + list[3]; 所以当循环执行闭包时,读取的变量i都是4,变量item都是’item’ + list[3]


参考:
http://coolshell.cn/articles/6731.html
http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html
https://zh.wikipedia.org/wiki/TypeScript
http://bonsaiden.github.io/JavaScript-Garden/zh/#function.closures