摘要:有关函数柯里化的详解,请回阅前端进击的巨人五学会函数柯里化。构造函数中的通过操作符可以实现对函数的构造调用。在了解构造函数中的前,有必要先了解下实例化对象的过程。
常见this的误解
指向函数自身(源于this英文意思的误解)
指向函数的词法作用域(部分情况)
this的应用环境 1. 全局环境无论是否在严格模式下,全局执行环境中(任何函数体外部)this都指向全局对象
var name = "以乐之名"; this.name; // 以乐之名
函数内部,this的值取决于函数被调用的方式(被谁调用)
var name = "无名氏"; function getName() { console.log(this.name); } getName(); // 无名氏 调用者是全局对象 var myInfo = { name: "以乐之名", getName: getName }; myInfo.getName(); // 以乐之名 调用者是myInfo对象this的正解
"this的指向是在运行时进行绑定的,而不是代码书写(函数声明)时确定!!!"
"看谁用",this的指向取决于调用者,这也是很多文章提到过的观点。"谁调用,this指向谁",只是这句话稍有偏颇,某些情况不见得都适用。
生活栗子:你的钱并不一定是你的钱,只有当你使用消费了才是你的钱 。
("看谁用"),借出去的钱就不是你的了。。。
回到正文,我们先通过栈,来理解什么是调用位置?
JavaScript中函数的调用是以栈的方式来存储,栈顶是正在运行的函数,函数调用时入栈,执行完成后出栈。
function foo() { // 此时的栈:全局 -> foo,调用位置在foo bar(); } function bar() { // 此时的栈:全局 -> foo -> bar,调用位置在bar baz(); } function baz() { // 此时的栈:全局 -> foo -> bar -> baz,调用位置在baz // ... } foo();
代码中虽然函数存在多层嵌套使用,但处于栈顶的只有正在执行的函数,也即调用者只有顶层的那一个(或最后一个),理清调用位置(调用者)有助于我们理解this。
this的绑定规则默认绑定(函数多带带调用)
隐式绑定(作为对象的属性方法调用,带有执行上下文)
显示绑定(call/apply/bind)
new绑定(new创建实例)
箭头函数绑定(ES6新增,基于词法作用域)
默认绑定下(函数多带带调用)区分严格模式非严格模式,this会指向全局对象(浏览器全局对象是window,NodeJS全局对象是global);
严格模式,this指向undefined
// 非严格模式 function getName() { console.log(this.name); // this指向全局对象 } getName(); // "",并不会报错,如果外部有全局变量name,则会输出对应值 // 严格模式 function getName() { "use strict" console.log(this.name); // this指向undefined } getName(); // TypeError: Cannot read property "name" of undefined
TIPS: 严格模式中,对函数中this的影响,只在函数内声明了严格模式才会存在,如果是调用时声明严格模式则不会影响。
function getName() { console.log(this.name); } // 调用时声明严格模式 "use strict"; getName(); // ""隐式绑定
隐式绑定中,函数一般作为对象的属性调用,带有调用者的执行上下文。因此this值取决于调用者的上下文环境。如果存在多层级属性引用,只有对象属性引用链中最顶层(最后一层)会影响调用位置,而this的值取决于调用位置。文章开头以栈来理解调用者的例子。
function getName() { return this.name; } var myInfo = { name: "以乐之名", getName: getName }; var leader = { name: "大神组长" man: myInfo }; leader.man.getName(); // "以乐之名" // man 指向 myInfo,最顶层(最后一层)对象为 myInfoapply/call的区别
apply/call方法两者类似,都可以显示绑定this,两者的区别是参数传递的方式不同。apply/call第一个参数都为要指定this的对象,不同的是apply第二个参数接受的是一个参数数组,而call从第二个参数开始接受的是参数列表。
apply语法:func.apply(thisArg, [argsArray])call语法:func.call(thisArg, arg1, arg2, ...)
var numbers = [5, 6, 2, 3, 7]; // 求numbers的最大值 // apply var max = Math.max.apply(null, numbers); // call var max = Math.max.call(null, ...numbers); // ...展开运算符
TIPS: 如果thisArg为原始值(数字,字符串,布尔值),this会指向该原始值的自动包装对象,如Number, String, Boolean等
func.apply(1); // func中的this -> Number对象;bind的特别(柯里化的应用)
bind是ES5新增的方法,跟apply/call功能一样,可以显示绑定this。
bind语法:function.bind(thisArg[, arg1[, arg2[, ...]]])bind()方法创建一个新的函数,在调用时设置this关键字为提供的值,并在调用新函数时,将给定参数列表作为原函数的参数序列的前若干项。
-- 《Function.prototype.bind() | MDN》
"bind与apply/call的区别:apply/call传入this并立即执行函数,而bind传入this则返回一个函数,并不会立即执行,只有调用返回的函数才会执行原始函数"。
bind方法是函数柯里化的一种应用,看过上篇《前端进击的巨人(五):学会函数柯里化(curry) 》的小伙伴,应该还记得"函数柯里化的特点:延迟执行,部分传参,返回一个可处理剩余参数的函数"。
bind相较apply/call的优点,可以通过部分传参提前对this进行一次"永久绑定",也就是说this只需绑定一次,省却每次执行都要进行this绑定的操作。
function getName() { return this.name; } var myInfo = { name: "以乐之名", job: "前端工程师" }; var getName = getName.bind(myInfo); getName(); // "以乐之名"; getName(); // "以乐之名"; // 一次性绑定,之后调用无需再修改this
TIPS: 函数柯里化可以用于参数预设,像一次性操作(判断/绑定)等。
有关函数柯里化的详解,请回阅:《前端进击的巨人(五):学会函数柯里化(curry) 》。
构造函数中的this通过new操作符可以实现对函数的构造调用。JavaScript中本身并没有"构造函数",一个函数如果没有使用new操作符调用,那么它就是个普通函数,new Func()实际上是对函数Func的"构造调用"。
在了解构造函数中的this前,有必要先了解下new实例化对象的过程。
new实例过程创建(构造)一个全新的空对象
这个新对象会被执行"原型"链接(新对象的__proto__会指向函数的prototype)
构造函数的this会指向这个新对象,并对this属性进行赋值
如果函数没有返回其他对象,则返回这个新对象(注意构造函数的return,一般不会有return)
// 正常不带return的构造函数 function People(name, sex) { this.name = name; this.sex = sex; } var man = new People("亚当", "男"); var woman = new People("夏娃", "女"); // 实例化对象成功
// 构造函数带了return function People(name, sex) { return 1; // 返回的是Number对象 } function People(name, sex) { return "hello world"; // 返回的是String对象 } function People(name, sex) { return function() {} } function People(name, sex) { return {}; } // 以上并未正确实例化对象
构造函数自定义return,会造成new无法完成正确的实例化操作。如果返回值为基本类型,则返回其包装对象Number/String/Bollean。
TIPS: 原型链中的this指向其实例化的对象
People.prototype.say = function() { console.log(`我的名字:${this.name}`); }; var man = new People("亚当", "男"); man.say(); // 我的名字:亚当this绑定规则的优先级
显示绑定 / new绑定 > 隐式绑定 > 默认绑定
TIPS: new无法跟apply/call同时使用
this判定步骤函数被new操作符使用(new绑定)? YES --> this绑定的是new创建的新对象
函数通过call/apply/bind(显示绑定)? YES --> this绑定的是指定的对象
函数在某个上下文对象中调用(隐式绑定)? YES --> this绑定的是那个上下文对象
默认绑定,严格模式指向undefined,否则指向全局对象
ES6的箭头函数(词法作用域的this机制,规则之外)箭头函数的this机制不同于传统的this机制,它采取的是另外一种机制,词法作用域的this判定规则。
// 例子一 var name = "无名氏"; var myInfo = { name: "以乐之名", getName: () => { console.log(this.name); } }; var getName = myInfo.getName; window.getName(); // 无名氏 myInfo.getName(); // 无名氏 // myInfo是在全局环境定义的,因此根据词法作用域,this指向全局对象 // 例子二 var name = "无名氏"; var myInfo = { name: "以乐之名", say: () => { setTimeout(() => { console.log(this.name); }) } }; myInfo.say(); // 无名氏 // 箭头函数通过作用域链来逐层查找this,最终找到全局变量myInfo,this指向全局对象 // 例子三 var name = "无名氏"; var myInfo = { name: "以乐之名", say: function() { setTimeout(() => { console.log(this.name); }) } }; myInfo.say(); // 以乐之名 // 箭头函数找到say: function(){},因此this的作用域来自myInfo
TIPS: setTimeout/setInterval/alert的调用者都是全局对象
"箭头函数的this始终指向函数定义时的this,而非执行(调用)时的this。箭头函数中的this必须通过作用域链一层一层向外查找,来确定this指向。"
扩展:箭头函数的书写规则// 函数表达式 const getName = (name) => { return "myName: " + name }; // 匿名函数 setTimeout((name) => { console.log(name); }, 1000)
// 只有一个参数 const getName = name => { return `myName: ${name}`; } // 无参数 const getName = () => { return "myName: "以乐之名""; } // 多参数 const getName = (firstName, lastName) => { return `myName: ${firstName} ${lastName}`; }
const getName = name => return `myName: ${name}`;
const getName = name => `myName: ${name}`;
参考文档:
你不知道的JavaScript(上卷)
彻底理解js中this的指向,不必硬背。
this|MDN
本文首发Github,期待Star!
https://github.com/ZengLingYong/blog
作者:以乐之名
本文原创,有不当的地方欢迎指出。转载请指明出处。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/108949.html
摘要:初始化队列初始化一个存储队列中元素的数据结构,如果未传入默认赋值空数组,传入需先校验类型是否正确。头等舱和商务舱乘客的优先级要高于经济舱乘客。在有些国家,老年人和孕妇或带小孩的妇女登机时也享有高于其他乘客的优先级。 有一天,当回顾自己走过的路时,你会发现这些奋斗不息的岁月,才是最美好的人生。——弗洛伊德 队列,英文 First In First Out 简称 FIFO,遵从先进先出的原...
摘要:超融合的持续演进与企业数字化转型的步调是一致的,这也是超融合受到业界追捧的原因。对于超融合市场的每一个参与者来说,目前最重要的任务是如何快速有效地获客。而这也正是超融合市场走向纵深所需的后劲儿,也是持续发展所需要的韧劲儿。超融合(HCI)市场可以一直火热,但超融合应用的落地最好理智而冷静。最热时,据说中国市场有上百家超融合供应商。随着超融合应用逐步落地,在市场和用户需求的双重考验下,有人离开...
摘要:当用户或搜索引擎向网站服务器发出浏览请求时,服务器返回的数据流中头信息中的状态码的一种,表示本网页永久性转移到另一个地址。通过在源代码中添加日志输出,我们也能清楚地看到的状态码。 Created 2019-4-5 22:24:33 by huqi Updated 2019-4-5 23:23:56 by huqi showImg(https://segmentfault.com/...
摘要:隆重请出主角防抖与节流。防抖与节流的异同相同都是防止某一时间段内,函数被频繁调用执行,通过时间频率控制,减少回调函数执行次数,来实现相关性能优化。参考文章分钟理解的节流防抖及使用场景函数防抖和节流 showImg(https://segmentfault.com/img/bVburM8?w=800&h=600); 本篇课题,或许早已是烂大街的解读文章。不过春招系列面试下来,不少伙伴们还...
阅读 2500·2023-04-26 02:57
阅读 1389·2023-04-25 21:40
阅读 2122·2021-11-24 09:39
阅读 3504·2021-08-30 09:49
阅读 714·2019-08-30 15:54
阅读 1147·2019-08-30 15:52
阅读 2030·2019-08-30 15:44
阅读 1258·2019-08-28 18:27