JS(JavaScript)中的this使用方式(call、apply、bind方法的异同)——大总结
2020-07-20 22:39
666 查看
文章目录
一,使用this的五种情况
- 如果this出现在普通的函数中,this表示window,如果通过window打点调用一个函数,这个函数中的this也是window;
- 事件绑定、事件处理程序、事件发生时、浏览器帮我们调用这个函数,此函数中的this表示事件源;
- 在一个对象中,有一个方法(函数),如果通过对象调用了这个方法,那么这个方法中的this就表示这个对象;
- 在IIFE(立即调用函数)中,this表示window;
- 前四点都是在非严格模式下,在严格模式下,调用一个普通函数,this表示undefined,IIFE中的this也表示undefined;
二,以下实例中包含的五种情况
- this表示事件源
<button id="box">box</button>--> // 一个公用的button标签(供一下实例中引用) <script> let box = document.getElementById("box"); box.onclick = function () { console.log(this); // this表示事件源 } </script>
let box = document.getElementById("box"); function f(){ console.log(this); // window return function g(){ console.log(this); // 事件源 } } box.onclick = f();
- this表示window
let box = document.getElementById("box"); function f() { console.log(this); // window } // f(); 程序员自己调用一个函数 一个普通函数 函数中的this表示window box.onclick = f();
function k() { console.log(this); // window } window.k(); // window打点调用 k(); // 程序员自己调用 表示window中的
- this表示对象
var wc = { name:"wangcai", // 叫属性 age:100, // 叫属性 eat:function () { // 叫方法(属性) console.log("eat...") console.log(this); 表示wc对象 } } // 一个方法中的this是谁,就看点前面是谁。 wc.eat(); // 调用一个对象上的方法
- 在非严格模式下,this表示window
(function () { console.log(this); })(); // 在非严格模式下,IIFE中的this表示window
- 当启用严格模式(“use strict”)下
"use strict"; // 启用JS的严格模式 function f() { console.log(this); //表示undefined } f(); (function () { console.log(this); //表示undefined })();
- 非严格模式下的几个实例:
function f() { console.log(this.name); // 表示window } let box = document.getElementById("box"); box.name = "mybox"; var name = "mywindow"; var obj = { name:"myobj", f:f } f(); // mywindow box.onclick = f; // 点击得到mybox //调用的是box对象中的属性 obj.f(); // myobj box.onmousedown = function () { //onmousedown表示鼠标放button上面就要执行的 f(); // 普通函数调用 }
- onclick是在鼠标点击弹起之后触发的事件,即一次完整的鼠标点击过程。过程完成瞬间触发函数;
- onmousedown事件是在鼠标按键按下去的瞬间触发的;
- 简而言之,也就是说οnclick=onmousedown+onmouseup;
var num = 10; //60 65 var obj = { num:20, // num:30 } obj.fn = (function (num) { this.num = num*3; // num++; // console.log(num) //21 return function (n) { this.num += n; // 30 num++; console.log(num) // 22 23 } })(obj.num)// 输出21 var fn = obj.fn; fn(5);// 输出22 obj.fn(10); //输出23 调用obj中的fn属性,其中fn指向function(n)的地址,里面的this.num中的this指向对象obj; console.log(num,obj.num) //输出(65,30) console.log(window.num) //输出 65
(function () { var a = 1; var obj = { a:10, // 要使用这个a 必须是obj.a f:function () { a *= 2; // a为局部变量a不是对象obj中的; } } obj.f(); alert(obj.a+a) // 运行结果为 12 })();
window.a = 1; var obj = { a:10, f:function () { this.a *= 2; console.log(this.a) //this指对象obj } } obj.f(); // 运行结果为 20
var name = "window"; var Wangcai = { name:"Wangcai", show:function () { console.log(this.name) }, f:function () { var fun = this.show; fun(); // 普通函数调用,对于普通函数调用,this就指向window } } Wangcai.f(); //输出window,
var fullname = "language"; var obj = { fullname:"javascript", prop:{ getFullName:function () { return this.fullname; } } } // 谁用了getFullName 看 . 前面是什么, . 前面是obj.prop,说明getFullName中的this是obj.prop;然而obj.prop中没有fullname属性;所以输出undefined,因为访问一个对象上没有的属性,得到undefined; console.log(obj.prop.getFullName()); // 输出 undefined
var fullname = "language"; var obj = { fullname:"javascript", prop:{ fullname:"vue", getFullName:function () { return this.fullname; } } } var t = obj.prop.getFullName; console.log(t()); //就是简单的函数执行;输出language
let obj = { fn:(function () { return function () { console.log(this) } })() } obj.fn(); //输出{fn: ƒ} fn()前面对应obj对象,所以在函数fn中的输出this指对象obj的;
let obj = { fn:(function () { return function () { console.log(this) } })() } var t = obj.fn; //t所获取的是function () { console.log(this) } t(); //只是简单的函数调用;
三, call,apply,bind使用的异同
首先说一下这三个方法的用途是用于修改this的指向的; 而且它们三个都可以修改;
- call的作用(1)可以改变this的指向;(2)让函数直接执行;(3)函数传参时,不是以数组的形式;
- apply的作用 (1)可以改变this的指向; (2)让函数直接执行;(3)函数传参时,以数组的形式;
- bind的作用 (1)可以改变this的指向; (2)不让函数执行;
那么问题来了:为什么要修改this指向?
从下面实例中来解释;
//存在两个堆, var obj1 = {name:"wc",say:function () {console.log("我是:"+this.name) }} var obj2 = {name:"xq",say:function () {console.log("我是:"+this.name) }} obj1.say(); obj2.say();
- 以上代码存在的不足:同样的函数,但是占用了两个堆空间,造成了内存的浪费;
但我们有改进的方法:
(1)改进一
var abc = function (){console.log("我是:"+this.name) } var obj1 = {name:"wc",say:abc} //此时say属性就指向堆空间; var obj2 = {name:"xq",say:abc} console.log(obj1.say === obj2.say)//输出结果为true,表明两个地址是一样的; obj1.say(); obj2.say(); abc();
(2)改进二(使用call)
var abc = function (){console.log("我是:"+this.name) } var obj1 = {name:"wc",say:abc} var obj2 = {name:"xq",say:abc} // call作用: 1)让abc中的this指向obj1 call改变this指向 2)让abc直接执行 调用 abc.call(obj1); // this==>obj1 abc() abc.call(obj2); //
在调用toString()方法时,我们可以使用call方法去直接调用Object上面的toString方法,写法
var arr3 = [110]; Object.prototype.toString.call(arr3); // arr3直接调用Object的原型对象上面的toString
此时call:(1)call让this指向了Object.prototype,(2)让toString执行;总之,call可以让一个数据去借用一个方法;
利用Object.prototype.toString.call可以非常精确地检测一个数据的数据类型:
console.log(Object.prototype.toString.call(1)); // 此时会把1瞬间包装成对象 console.log(Object.prototype.toString.call("hello")); // 此时"hello"也瞬间包装成对象了 console.log(Object.prototype.toString.call(true)) var a; console.log(Object.prototype.toString.call(a)) // call 1)改变toString中的this的指向 指向了{} // 2)让toString执行 console.log(Object.prototype.toString.call({})) console.log(Object.prototype.toString.call([])) function f(){} console.log(Object.prototype.toString.call(f)) let d = new Date() console.log(Object.prototype.toString.call(d)) let r = new RegExp(); console.log(Object.prototype.toString.call(r))
下面代码可以将一段字符串截取出来,并且变为小写(用于将上面的检测类型给截取下来变为小写并输出):
function getType(abc) { var rs = Object.prototype.toString.call(abc); rs = rs.substr(8) var len = rs.length return rs.substr(0,len-1).toLowerCase()//变为小写 } console.log(getType(123));//将数据类型修改输出
总之: call方法的两大作用如下
- 修改了this的指向;
- 可以让方法直接执行;
相对于call方法来说,apply有什么不一样呢?
可以说apply和call是一模一样的,唯一唯一的不同就是传递参数时候存在不一样;如下:
- 分别使用call和apply的时候:
function F(a,b) { return a+b; } var obj = {}; F.call(obj); // 让F执行,关键是我们如何给F传递参数 F(1,2); // 我们自己调用F函数 并传递参数 console.log(F.call(obj,4,5)); // 我们使用call来传参的方式; console.log(F.apply(obj,[4,5])); // 而使用apply来传参的方式;
- 再如:
var arr = [2,3,3,6,9,10,2,1]; // 获取上面的数组中的最大值 Math类(最好不要叫它为构造器) // console.log(Math.max(2, 4, 1, 6)); console.log(Math.max.call(arr, 2, 3, 3, 6, 9, 10, 2, 1));//使用call传参 console.log(Math.max.apply(arr, [2,3,3,6,9,10,2,1]));//使用apply传参 console.log(Math.max.apply(arr, arr));//使用apply传参 console.log(Math.max.apply({}, arr)); console.log(Math.max.apply("", arr)); console.log(Math.max.apply(null, arr)); console.log(Math.max.apply("abc", arr)); //上面表明如使用apply方法的时候,不过第一个参数写啥,只要把实参传过去就OK,如传一个数值的话使用apply效果会好点;
对于bind方法来说,与上面两种方法不同的地方在于;
call 改变了this指向,并让函数执行;传递参数一个个传递;
apply 改变了this指向,并让函数执行;传送参数必须是数组;
bind 改变了this指向,但是函数不执行;
function F(a,b) { console.log("F...") return a+b; } var obj = {}; let k = F.bind(obj);//bind改变的知识this的指向,而函数不执行,要想执行还需要函数调用并传参; console.log(k(1, 2));
相关文章推荐
- JavaScript中call,apply,bind方法的总结(改变this指向)
- [JS]JS中call/apply/bind方法总结
- [置顶] JavaScript中call,apply,bind方法的总结
- JavaScript中的call(),apply(),bind()方法 关于this指向
- JavaScript方法call,apply,caller,callee,bind的使用详解及区别
- JS中改变this指向的方法(call和apply、bind)
- Javascript中call,apply,bind方法的详解与总结
- javascript中call、apply、bind方法的使用
- JavaScript方法call,apply,caller,callee,bind的使用详解及区别
- JavaScript方法call、apply、caller、callee、bind的使用详解及区别
- JavaScript方法call,apply,caller,callee,bind的使用详解及区别
- JavaScript中call,apply,bind方法的总结。
- js apply/call/caller/callee/bind使用方法与区别分析
- JavaScript中call,apply,bind方法的总结
- Javascript中call,apply,bind方法的详解与总结
- js apply/call/caller/callee/bind使用方法与区别分析
- js面向对象||this指向||call和apply方法的使用和区别
- Javascript中call,apply,bind方法的详解与总结
- js: this,call,apply,bind 总结
- JS修改this的方法bind、apply、call的理解