Home > Article > Web Front-end > Elegant use of This in js
This time I will bring you the elegant use of This in js. What are the precautions for the elegant use of This in js? The following is a practical case, let's take a look.
When a function is called, an activity record (execution context) is created.In strict mode, this points to undefined when a general function is called.
eg:
var me = { name: "fenfei" }; //不使用this,调用 function speak(name){ console.log("Hello, I'm "+ name); } speak(me.name); //Hello, I'm fenfei //使用this,调用 function speak(){ console.log("Hello, I'm "+ this.name); } speak.call(me); //Hello, I'm fenfei
(2) this Points to the scope of the function.
eg:
function foo() { console.log(this.a); }var a = 2; foo(); // 22) Implicit rules The rule of implicit binding is whether there is a context object at the calling location, or whether it is owned or included by an object.
function foo() { console.log(this.a); }var obj = { a: 2, foo: foo }; obj.foo(); // 2. When foo() is called, this is bound to obj, so this.a and obj.a are the same. But sometimes implicit loss occurs.
function foo() { console.log(this.a); }var obj = { a: 2, foo: foo };var bar = obj.foo; // 函数 var a = "oops, global"; //bar(); // "oops, global". Although bar is a reference to obj.foo, it actually refers to the foo function itself.
So bar() at this time is actually a function call without any modification, and the default binding is applied.
3) Display binding
. In JavaScript, call and apply are like this's parents. They must live wherever this lives, and they have to obey! When there are no parameters, the current object is window
eg:
var name="全局";var xpg={ name:"局部"};function getName(){ alert(this.name); } getName(xpg);//全局getName.call(xpg);//局部getName.call();//全局. Among them, this is in the function getName. No matter where this is, you must find the location when the function is running. At this time, the position of the function getName when it is running, for
(1) getName(xpg); //Global
Obviously, the object where the function getName is located is window, so the home of this must be in the window, that is, pointing to
window object, then this.name returned by getName is actually window.name, so the alert comes out as "global"! (2)getName.call(xpg);//Partial
. Among them, call specifies that the home of this is the xpg object, because this is forced to settle only in xpg, then this points to the xpg object at this time, this.name is actually xpg.name, so the alert comes out as "local" !
. The bind method is provided starting from es5, so ie9 only supports
eg:
function f(){ return this.a; } var g = f.bind({a : "test"}); //想把某个对象作为this的时候,就把它传进去,得到一个新对象gconsole.log(g()); // test //重复调用的时候,this已经指向bind参数。 //这对于我们绑定一次需要重复调用依然实现绑定的话,会比apply和call更加高效(看下面这个例子)var o = {a : 37, f : f, g : g};console.log(o.f(), o.g()); // 37, test //o.f() passes the object Attribute call, this points to object o; //What is special is that even if we call the newly bound method as an attribute of the object, //o.g() will still follow the previous binding, so the answer is test is not g
(1) Create (or construct) a brand new object;
(2) This new object will be connected by executing [[Prototype]] ;
(3) This new object will be bound to this of the function call;
(4) If the function does not return other objects, then the function call in the new expression will automatically return this new object.
eg:
function Person(name,age) { this.name = name this.age = age console.log("我也只不过是个普通函数") } Person("zxt",22) // "我也只不过是个普通函数"console.log(name) // "zxt"console.log(age) // 22var zxt = new Person("zxt",22) // "我也只不过是个普通函数"console.log(zxt.name) // "zxt"console.log(zxt.age) // 22In the above example, a Person function is first defined, which can be called either normally or in the form of a
constructor. . When called normally, it will be executed as a normal function and a string will be output.
. If it is passed through a new operator, a new object is constructed.
. When making a normal call, as mentioned before, the default binding rules are applied and this is bound to the global object. At this time, two attributes, name and age, will be added to the global object.
. When called through the new operator, the function will return an object. From the output result, it can be seen that the this object is bound to the returned object.
了解了函数调用中this绑定的四条规则,需要做的就是找到函数的调用位置并判断对应哪条规则。
(1)函数是否是new绑定?如果是,this绑定的是新创建的对象。
var bar = new Foo();
(2)函数是否通过call、apply显示绑定或硬绑定?如果是,this绑定的是指定的对象。
var bar = foo.call(obj);
(3)函数是否在某个上下文对象中隐式调用?如果是,this绑定的是那个上下文对象。
var bar = obj.foo();
(4)上述全不是,则使用默认绑定。如果在严格模式下,就绑定到undefined,否则绑定到全局window对象。
var bar = foo();
new绑定和call、apply无法一起使用,因此不能使用new foo.call(obj).
五、this绑定例外
1)被忽略的绑定
如果你把null或者undefined作为this的绑定对象传入call、apply或者bind。
这些值在调用时会被忽略,实际应用的是默认绑定规则。
eg:
function foo() { console.log(this.a); }var a = 2; foo.call(null); // 2
2)间接引用
eg:
function foo() { console.log(this.a); }var a = 2; var o = { a: 3, foo: foo }; var p = { a: 4 }; o.foo(); // 3(p.foo = o.foo)(); // 2
赋值表达式p.foo = o.foo的返回值是目标函数的引用,因此调用位置是foo()而不是p.foo()或者o.foo()。
3)当前对象不明确时的this
当没有明确的执行时的当前对象时,this指向全局对象window。
例如对于全局变量引用的函数上我们有:
var name = "Tom";var Bob = { name: "Bob", show: function(){ alert(this.name); } }var show = Bob.show; show(); //Tom
。你可能也能理解成show是window对象下的方法,所以执行时的当前对象时window。但局部变量引用的函数上,却无法这么解释:
var name = "window";var Bob = { name: "Bob", showName: function(){ alert(this.name); } };var Tom = { name: "Tom", showName: function(){ var fun = Bob.showName; fun(); } }; Tom.showName(); //window
4)在浏览器中setTimeout、setInterval和匿名函数执行时的当前对象是全局对象window:
var name = "Bob"; var nameObj ={ name : "Tom", showName : function(){ alert(this.name); }, waitShowName : function(){ setTimeout(this.showName, 1000); } }; nameObj.waitShowName();
5)软绑定
eg:
var count=2;var obj={ count:0, cool:function coolFn(){ console.log(this.count);//0 var self=this; if(self.count<1){ setTimeout(function timer(){ self.count++; console.log("awesome?"); console.log(self.count);//1 console.log(this.count);//2 },100); } } }; obj.cool();
6)dom事件中的this
(1)直接在dom元素中使用
1eb9ebab541c78a07af4b7a6c86d652c
分析:对于dom元素的一个onclick(或其他如onblur等)属性,它为所属的html元素所拥有,直接在它触发的函数里写this,this应该指向该html元素。
(2)给dom元素注册js函数
a、不正确的方式
<script type="text/javascript"> function thisTest(){ alert(this.value); // 弹出undefined, this在这里指向??}</script><input id="btnTest" type="button" value="提交" onclick="thisTest()" />
。分析:onclick事件直接调用thisTest函数,程序就会弹出undefined。
因为thisTest函数是在window对象中定义的, 所以thisTest的拥有者(作用域)是window,thisTest的this也是window。而window是没有value属性的,所以就报错了。
b、正确的方式
<input id="btnTest" type="button" value="提交" /><script type="text/javascript"> function thisTest(){ alert(this.value); }document.getElementById("btnTest").onclick=thisTest; //给button的onclick事件注册一个函数</script>
。分析:在前面的示例中,thisTest函数定义在全局作用域(这里就是window对象),所以this指代的是当前的window对象。
而通过document.getElementById(“btnTest”).onclick=thisTest;这样的形式,其实是将btnTest的onclick属性设置为thisTest函数的一个副本,在btnTest的onclick属性的函数作用域内,this归btnTest所有,this也就指向了btnTest。
因为多个不同的HTML元素虽然创建了不同的函数副本,但每个副本的拥有者都是相对应的HTML元素,各自的this也都指向它们的拥有者,不会造成混乱。
eg:
<input id="btnTest1" type="button" value="提交1" onclick="thisTest()" /><input id="btnTest2" type="button" value="提交2" /><script type="text/javascript">function thisTest(){this.value="提交中"; }var btn=document.getElementById("btnTest1"); alert(btn.onclick); //第一个按钮函数var btnOther=document.getElementById("btnTest2"); btnOther.onclick=thisTest; alert(btnOther.onclick); //第二个按钮函数</script>
其弹出的结果是:
//第一个按钮function onclick(){ thisTest() }//第二个按钮function thisTest(){ this.value="提交中"; }
7)this词法(ES6:箭头函数)
箭头函数不使用function关键字定义,而是使用“胖箭头”的操作符=>定义;箭头函数不使用this的四种标准规则,而是根据外层(函数或者全局)作用域来决定this。
eg:
function foo(){ return (a)=>{ //this继承自foo console.log(this.a); }; }var obj1={ a:2}var obj2={ a:3}var bar=foo.call(obj1); bar.call(obj2);//2不是3!
foo()内部创建的箭头函数会捕获调用时foo()的this。由于foo()的this被绑定到obj1,bar(引用箭头函数)的this也被绑定到obj1,而箭头函数的绑定无法修改。(new的也不能!)
相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!
推荐阅读:
The above is the detailed content of Elegant use of This in js. For more information, please follow other related articles on the PHP Chinese website!