当前位置: 首页 > news >正文

衡水网站seo宁波seo外包sem

衡水网站seo,宁波seo外包sem,网页设计与制作课程的建设历程,网站备案做优惠券this 指向问题 前言 当一个函数调用时#xff0c;会创建一个执行上下文#xff0c;这个上下文包括函数调用的一些信息(调用栈#xff0c;传入参数#xff0c;调用方式)#xff0c;this就指向这个执行上下文。 this 不是静态的#xff0c;也并不是在编写的时候绑定的会创建一个执行上下文这个上下文包括函数调用的一些信息(调用栈传入参数调用方式)this就指向这个执行上下文。 this 不是静态的也并不是在编写的时候绑定的而是在运行时绑定的。它的绑定和函数声明的位置没有关系只取决于函数调用的方式。 this 指向哪里 在JavaScript中要想完全理解this首先要理解this的绑定规则this的绑定规则一共有 5 种 默认绑定隐式绑定显式(硬)绑定new绑定ES6新增箭头函数绑定 下面来一一介绍以下this的绑定规则。 1.默认绑定 默认绑定通常是指函数独立调用不涉及其他绑定规则。非严格模式下this指向window严格模式下this指向undefined。 题目 1.1非严格模式 var foo 123 function print() {this.foo 234console.log(this) // windowconsole.log(foo) // 234 } print()非严格模式print()为默认绑定this指向window所以打印window和234。 这个foo值可以说道两句如果学习过预编译的知识在预编译过程中foo和print函数会存放在全局GO中(即window对象上)所以上述代码就类似下面这样 window.foo 123 function print() {this.foo 234console.log(this)console.log(window.foo) } window.print()题目 1.2严格模式 把题目1.1稍作修改看看严格模式下的执行结果。 use strict可以开启严格模式 use strict var foo 123 function print() {console.log(print this is , this)console.log(window.foo)console.log(this.foo) } console.log(global this is , this) print()注意事项开启严格模式后函数内部this指向undefined但全局对象window不会受影响 答案 global this is Window{...} print this is undefined 123 Uncaught TypeError: Cannot read property foo of undefined题目 1.3let/const let a 1 const b 2 var c 3 function print() {console.log(this.a)console.log(this.b)console.log(this.c) } print() console.log(this.a)let/const定义的变量存在暂时性死区而且不会挂载到window对象上因此print中是无法获取到a和b的。 答案 undefined undefined 3 undefined题目 1.4对象内执行 a 1 function foo() {console.log(this.a) } const obj {a: 10,bar() {foo() // 1}, } obj.bar()foo虽然在obj的bar函数中但foo函数仍然是独立运行的foo中的this依旧指向window对象。 题目 1.5函数内执行 var a 1 function outer() {var a 2function inner() {console.log(this.a) // 1}inner() } outer()这个题与题目1.4类似但要注意不要把它看成闭包问题。 题目 1.6自执行函数 a 1 ;(function () {console.log(this)console.log(this.a) })() function bar() {b 2;(function () {console.log(this)console.log(this.b)})() } bar()默认情况下自执行函数的this指向window 自执行函数只要执行到就会运行并且只会运行一次this指向window。 答案 Window{...} 1 Window{...} 2 // b是imply global会挂载到window上暗示全局变量 imply global : 即任何变量如果变量未经声明赋值此变量就为全局对象(window)所有 2.隐式绑定 函数的调用是在某个对象上触发的即调用位置存在上下文对象通俗点说就是**XXX.func()**这种调用模式。 此时func的this指向XXX但如果存在链式调用例如XXX.YYY.ZZZ.func记住一个原则this 永远指向最后调用它的那个对象。 题目 2.1隐式绑定 var a 1 function foo() {console.log(this.a) } // 对象简写等同于 {a:2, foo: foo} var obj { a: 2, foo } foo() obj.foo()foo(): 默认绑定打印1obj.foo(): 隐式绑定打印2 答案 1 2obj是通过var定义的obj会挂载到window之上的obj.foo()就相当于window.obj.foo()这也印证了this 永远指向最后调用它的那个对象规则。 题目 2.2对象链式调用 感觉上面总是空谈链式调用的情况下面直接来看一个例题 var obj1 {a: 1,obj2: {a: 2,foo() {console.log(this.a)},}, } obj1.obj2.foo() // 23.隐式绑定的丢失 隐式绑定可是个调皮的东西一不小心它就会发生绑定的丢失。一般会有两种常见的丢失 使用另一个变量作为函数别名之后使用别名执行函数将函数作为参数传递时会被隐式赋值 隐式绑定丢失之后this的指向会启用默认绑定。 具体来看题目 题目 3.1取函数别名 a 1 var obj {a: 2,foo() {console.log(this.a)}, } var foo obj.foo obj.foo() foo()JavaScript对于引用类型其地址指针存放在栈内存中真正的本体是存放在堆内存中的。 上面将obj.foo赋值给foo就是将foo也指向了obj.foo所指向的堆内存此后再执行foo相当于直接执行的堆内存的函数与obj无关foo为默认绑定。笼统的记只要 fn 前面什么都没有肯定不是隐式绑定。 答案 2 1不要把这里理解成window.foo执行如果foo为let/const定义foo不会挂载到window上但不会影响最后的打印结果 题目 3.2取函数别名 如果取函数别名没有发生在全局而是发生在对象之中又会是怎样的结果呢 var obj {a: 1,foo() {console.log(this.a)} } var a 2 var foo obj.foo var obj2 { a: 3, foo: obj.foo }obj.foo() foo() obj2.foo()/obj2.foo指向了obj.foo的堆内存此后执行与obj无关(除非使用call/apply改变this指向) 答案 1 2 3题目 3.3函数作为参数传递 function foo() {console.log(this.a) } function doFoo(fn) {console.log(this)fn() } var obj { a: 1, foo } var a 2 doFoo(obj.foo)用函数预编译的知识来解答这个问题函数预编译四部曲前两步分别是 找形参和变量声明值赋予undefined将形参与实参相统一也就是将实参的值赋予形参。 obj.foo作为实参在预编译时将其值赋值给形参fn是将obj.foo指向的地址赋给了fn此后fn执行不会与obj产生任何关系。fn为默认绑定。 答案 Window {…} 2题目 3.4函数作为参数传递 将上面的题略作修改doFoo不在window上执行改为在obj2中执行 function foo() {console.log(this.a) } function doFoo(fn) {console.log(this)fn() } var obj { a: 1, foo } var a 2 var obj2 { a: 3, doFoo } obj2.doFoo(obj.foo)console.log(this): obj2.doFoo符合xxx.fn格式doFoo的为隐式绑定this为obj2打印{a: 3, doFoo: ƒ}fn(): 没有于obj2产生联系默认绑定打印 2 答案 {a: 3, doFoo: ƒ} 2题目 3.5回调函数 下面这个题目我们写代码时会经常遇到 var name zcxiaobao function introduce() {console.log(Hello,My name is , this.name) } const Tom {name: TOM,introduce: function () {setTimeout(function () {console.log(this)console.log(Hello, My name is , this.name)})}, } const Mary {name: Mary,introduce, } const Lisa {name: Lisa,introduce, }Tom.introduce() setTimeout(Mary.introduce, 100) setTimeout(function () {Lisa.introduce() }, 200)setTimeout是异步调用的只有当满足条件并且同步代码执行完毕后才会执行它的回调函数。 Tom.introduce()执行: console位于setTimeout的回调函数中回调函数的this指向windowMary.introduce直接作为setTimeout的函数参数(类似题目题目3.3)会发生隐式绑定丢失this为默认绑定Lisa.introduce执行虽然位于setTimeout的回调函数中但保持xxx.fn模式this为隐式绑定。 答案 Window {…} Hello, My name is zcxiaobao Hello,My name is zcxiaobao Hello,My name is Lisa所以如果我们想在setTimeout或setInterval中使用外界的this需要提前存储一下避免this的丢失。 const Tom {name: TOM,introduce: function () {_self thissetTimeout(function () {console.log(Hello, My name is , _self.name)})}, } Tom.introduce()题目 3.6隐式绑定丢失综合题 name javascript let obj {name: obj,A() {this.name thisconsole.log(this.name)},B(f) {this.name thisf()},C() {setTimeout(function () {console.log(this.name)}, 1000)}, } let a obj.A a() obj.B(function () {console.log(this.name) }) obj.C() console.log(name)答案 javascriptthis javascriptthis javascriptthis javascriptthis4.显式绑定 显式绑定比较好理解就是通过call()、apply()、bind()等方法强行改变this指向。 上面的方法虽然都可以改变this指向但使用起来略有差别 call()和apply()函数会立即执行bind()函数会返回新函数不会立即执行函数call()和apply()的区别在于call接受若干个参数apply接受数组。 题目 4.1比较三种调用方式 function foo() {console.log(this.a) } var obj { a: 1 } var a 2foo() foo.call(obj) foo.apply(obj) foo.bind(obj)foo(): 默认绑定。foo.call(obj): 显示绑定foo的this指向objfoo.apply(obj): 显式绑定foo.bind(obj): 显式绑定但不会立即执行函数没有返回值 答案 2 1 1题目 4.2隐式绑定丢失 题目3.4发生隐式绑定的丢失如下代码我们可不可以通过显式绑定来修正这个问题。 function foo() {console.log(this.a) } function doFoo(fn) {console.log(this)fn() } var obj { a: 1, foo } var a 2 doFoo(obj.foo)首先先修正doFoo()函数的this指向。 doFoo.call(obj, obj.foo)然后修正fn的this。 function foo() {console.log(this.a) } function doFoo(fn) {console.log(this)fn.call(this) } var obj { a: 1, foo } var a 2 doFoo(obj.foo)大功告成。 题目 4.3回调函数与 call 接着上一个题目的风格稍微变点花样 var obj1 {a: 1, } var obj2 {a: 2,bar: function () {console.log(this.a)},foo: function () {setTimeout(function () {console.log(this)console.log(this.a)}.call(obj1),0)}, } var a 3 obj2.bar() obj2.foo()乍一看上去这个题看起来有些莫名其妙setTimeout那是传了个什么东西 做题之前先了解一下setTimeout的内部机制 setTimeout(fn) {if (回调条件满足) (fn) }这样一看本题就清楚多了类似题目4.2修正了回调函数内fn的this指向。 答案 2 {a: 1 } 1题目 4.4注意 call 位置 function foo() {console.log(this.a) } var obj { a: 1 } var a 2foo() foo.call(obj) foo().call(obj)foo(): 默认绑定foo.call(obj): 显式绑定foo().call(obj): 对foo()执行的返回值执行callfoo返回值为undefined执行call()会报错 答案 2 1 2 Uncaught TypeError: Cannot read property call of undefined题目 4.5注意 call 位置(2) 上面由于foo没有返回函数无法执行call函数报错因此修改一下foo函数让它返回一个函数。 function foo() {console.log(this.a)return function () {console.log(this.a)} } var obj { a: 1 } var a 2foo() foo.call(obj) foo().call(obj)foo(): 默认绑定foo.call(obj): 显式绑定foo().call(obj): foo()执行打印2返回匿名函数通过call将this指向obj打印1。 这里千万注意最后一个foo().call(obj)有两个函数执行会打印2 个值。 答案 2 1 2 1题目 4.6bind 将上面的call全部换做bind函数又会怎样那 call 是会立即执行函数bind 会返回一个新函数但不会执行函数 function foo() {console.log(this.a)return function () {console.log(this.a)} } var obj { a: 1 } var a 2foo() foo.bind(obj) foo().bind(obj)首先我们要先确定最后会输出几个值bind不会执行函数因此只有两个foo()会打印a。 foo(): 默认绑定打印2foo.bind(obj): 返回新函数不会执行函数无输出foo().bind(obj): 第一层foo()默认绑定打印2后bind将foo()返回的匿名函数this指向obj不执行 答案 2 2题目 4.7外层 this 与内层 this 做到这里不由产生了一些疑问如果使用call、bind等修改了外层函数的this那内层函数的this会受影响吗(注意区别箭头函数) function foo() {console.log(this.a)return function () {console.log(this.a)} } var obj { a: 1 } var a 2 foo.call(obj)()foo.call(obj): 第一层函数foo通过call将this指向obj打印1第二层函数为匿名函数默认绑定打印2。 答案 1 2题目 4.8对象中的 call 把上面的代码移植到对象中看看会发生怎样的变化 var obj {a: obj,foo: function () {console.log(foo:, this.a)return function () {console.log(inner:, this.a)}}, } var a window var obj2 { a: obj2 }obj.foo()() obj.foo.call(obj2)() obj.foo().call(obj2)看着这么多括号是不是感觉有几分头大。没事咱们来一层一层分析 obj.foo()(): 第一层obj.foo()执行为隐式绑定打印出foo:obj第二层匿名函数为默认绑定打印inner:windowobj.foo.call(obj2)(): 类似题目4.7第一层obj.foo.call(obj2)使用call将obj.foo的this指向obj2打印foo: obj2第二层匿名函数默认绑定打印inner:windowobj.foo().call(obj2): 类似题目4.5第一层隐式绑定打印foo: obj第二层匿名函数使用call将this指向obj2打印inner: obj2 题目 4.9带参数的 call 显式绑定一开始讲的时候就谈过call/apply存在传参差异那咱们就来传一下参数看看传完参数的 this 会是怎样的美妙。 var obj {a: 1,foo: function (b) {b b || this.areturn function (c) {console.log(this.a b c)}}, } var a 2 var obj2 { a: 3 }obj.foo(a).call(obj2, 1) obj.foo.call(obj2)(1)要注意call执行的位置 obj.foo(a).call(obj2, 1): obj.foo(a): foo 的 AO 中 b 值为传入的 a(形参与实参相统一)值为 2返回匿名函数 fn匿名函数fn.call(obj2, 1): fn 的 this 指向为 obj2c 值为 1this.a b c obj2.a FooAO.b c 3 2 1 6 obj.foo.call(obj2)(1): obj.foo.call(obj2): obj.foo 的 this 指向 obj2未传入参数b this.a obj2.a 3返回匿名函数 fn匿名函数fn(1): c 1默认绑定this 指向 windowthis.a b c window.a obj2.a c 2 3 1 6 答案 6 65.显式绑定扩展 上面提了很多call/apply可以改变this指向但都没有太多实用性。下面来一起学几个常用的call与apply使用。 题目 5.1apply 求数组最值 JavaScript 中没有给数组提供类似 max 和 min 函数只提供了Math.max/min用于求多个数的最值所以可以借助 apply 方法直接传递数组给Math.max/min const arr [1, 10, 11, 33, 4, 52, 17] Math.max.apply(Math, arr) Math.min.apply(Math, arr)题目 5.2类数组转为数组 ES6未发布之前没有Array.from方法可以将类数组转为数组采用Array.prototype.slice.call(arguments)或[].slice.call(arguments)将类数组转化为数组。 题目 5.3数组高阶函数 日常编码中我们会经常用到forEach、map等但这些数组高阶方法它们还有第二个参数thisArg每一个回调函数都是显式绑定在thisArg上的。 例如下面这个例子 const obj { a: 10 } const arr [1, 2, 3, 4] arr.forEach(function (val, key) {console.log(${key}: ${val} --- ${this.a}) }, obj)答案 0: 1 --- 10 1: 2 --- 10 2: 3 --- 10 3: 4 --- 106.new 绑定 使用new来构建函数会执行如下四部操作 创建一个空的简单JavaScript对象即{}为步骤 1 新创建的对象添加属性__proto__将该属性链接至构造函数的原型对象 将步骤 1 新创建的对象作为this的上下文 如果该函数没有返回对象则返回this。 通过 new 来调用构造函数会生成一个新对象并且把这个新对象绑定为调用函数的 this。 题目 6.1new 绑定 function User(name, age) {this.name namethis.age age } var name Tom var age 18var zc new User(zc, 24) console.log(zc.name)答案 zc题目 6.2属性加方法 function User(name, age) {this.name namethis.age agethis.introduce function () {console.log(this.name)}this.howOld function () {return function () {console.log(this.age)}} } var name Tom var age 18 var zc new User(zc, 24) zc.introduce() zc.howOld()()这个题很难不让人想到如下代码都是函数嵌套具体解法是类似的可以对比来看一下啊。 const User {name: zc;age: 18;introduce function () {console.log(this.name)}howOld function () {return function () {console.log(this.age)}} } var name Tom; var age 18; User.introduce() User.howOld()()zc.introduce(): zc 是 new 创建的实例this 指向 zc打印zczc.howOld()(): zc.howOld()返回一个匿名函数匿名函数为默认绑定因此打印 18(阿包永远18) 答案 zc 18题目 6.3new 界的天王山 new界的天王山每次看懂后没过多久就会忘掉但这次要从根本上弄清楚该题。 接下来一起来品味品味 function Foo() {getName function () {console.log(1)}return this } Foo.getName function () {console.log(2) } Foo.prototype.getName function () {console.log(3) } var getName function () {console.log(4) } function getName() {console.log(5) }Foo.getName() getName() Foo().getName() getName() new Foo.getName() new Foo().getName() new new Foo().getName()预编译 GO {Foo: fn(Foo),getName: function getName(){ console.log(5) }; }分析后续执行 Foo.getName(): 执行 Foo 上的 getName 方法打印2 getName(): 执行 GO 中的 getName 方法打印4 Foo().getName() // 修改全局GO的getName为function(){ console.log(1); } getName function () {console.log(1) } // Foo为默认绑定this - window // return window return this 复制代码Foo().getName(): 执行 window.getName()打印1Foo()执行 getName(): 执行 GO 中的 getName打印1 分析后面三个打印结果之前先补充一些运算符优先级方面的知识 从上图可以看到部分优先级如下new(带参数列表) 成员访问 函数调用 new(不带参数列表) new Foo.getName() 首先从左往右看new Foo属于不带参数列表的 new(优先级19)Foo.getName属于成员访问(优先级20)getName()属于函数调用(优先级20)同样优先级遵循从左往右执行。 Foo.getName执行获取到 Foo 上的getName属性此时原表达式变为new (Foo.getName)()new (Foo.getName)()为带参数列表(优先级20)(Foo.getName)()属于函数调用(优先级20)从左往右执行new (Foo.getName)()执行打印2并返回一个以Foo.getName()为构造函数的实例 这里有一个误区很多人认为这里的new是没做任何操作的的执行的是函数调用。那么如果执行的是Foo.getName()调用返回值为undefinednew undefined会发生报错并且我们可以验证一下该表达式的返回结果。 console.log(new Foo.getName()) // 2 // Foo.getName {}可见在成员访问之后执行的是带参数列表格式的 new操作。 new Foo().getName() 同步骤4一样分析先执行new Foo()返回一个以Foo为构造函数的实例Foo的实例对象上没有getName方法沿原型链查找到Foo.prototype.getName方法打印3 new new Foo().getName() 从左往右分析: 第一个 new 不带参数列表(优先级19)new Foo()带参数列表(优先级20)剩下的成员访问和函数调用优先级都是20 new Foo()执行返回一个以Foo为构造函数的实例在执行成员访问Foo实例对象在Foo.prototype查找到getName属性执行new (new Foo().getName)()返回一个以 Foo.prototype.getName()为构造函数的实例打印3 new Foo.getName() 与 new new Foo().getName()区别 new Foo.getName()的构造函数是Foo.getNamenew new Foo().getName()的构造函数为Foo.prototype.getName 测试结果如下: foo1 new Foo.getName() foo2 new new Foo().getName() console.log(foo1.constructor) console.log(foo2.constructor)输出结果 2 3 ƒ (){ console.log(2); } ƒ (){ console.log(3); }通过这一步比较应该能更好的理解上面的执行顺序。 答案 2 4 1 1 2 3 37.箭头函数 箭头函数没有自己的this它的this指向外层作用域的this且指向函数定义时的this而非执行时。 this指向外层作用域的this: 箭头函数没有this绑定但它可以通过作用域链查到外层作用域的this指向函数定义时的this而非执行时: JavaScript是静态作用域就是函数定义之后作用域就定死了跟它执行时的地方无关。 题目 7.1对象方法使用箭头函数 name tom const obj {name: zc,intro: () {console.log(My name is this.name)}, } obj.intro()上文说到箭头函数的this通过作用域链查到intro函数的上层作用域为window。 答案 My name is tom题目 7.2箭头函数与普通函数比较 name tom const obj {name: zc,intro: function () {return () {console.log(My name is this.name)}},intro2: function () {return function () {console.log(My name is this.name)}}, } obj.intro2()() obj.intro()()obj.intro2()(): 不做赘述打印My name is tomobj.intro()(): obj.intro()返回箭头函数箭头函数的this取决于它的外层作用域因此箭头函数的this指向obj打印My name is zc 题目 7.3箭头函数与普通函数的嵌套 name window const obj1 {name: obj1,intro: function () {console.log(this.name)return () {console.log(this.name)}}, } const obj2 {name: obj2,intro: () {console.log(this.name)return function () {console.log(this.name)}}, } const obj3 {name: obj3,intro: () {console.log(this.name)return () {console.log(this.name)}}, }obj1.intro()() obj2.intro()() obj3.intro()()obj1.intro()(): 类似题目7.2打印obj1obj1obj2.intro()(): obj2.intro()为箭头函数this为外层作用域this指向window。返回匿名函数为默认绑定。打印windowwindowobj3.intro()(): obj3.intro()与obj2.intro()相同返回值为箭头函数外层作用域intro的this指向window打印windowwindow 答案 obj1 obj1 window window window window题目 7.4new 碰上箭头函数 function User(name, age) {this.name namethis.age age;(this.intro function () {console.log(My name is this.name)}),(this.howOld () {console.log(My age is this.age)}) }var name Tom,age 18 var zc new User(zc, 24) zc.intro() zc.howOld()zc是new User实例因此构造函数User的this指向zczc.intro(): 打印My name is zczc.howOld(): howOld为箭头函数箭头函数this 由外层作用域决定且指向函数定义时的 this外层作用域为Userthis指向zc打印My age is 24 题目 7.5call 碰上箭头函数 箭头函数由于没有this不能通过call\apply\bind来修改this指向但可以通过修改外层作用域的this来达成间接修改 var name window var obj1 {name: obj1,intro: function () {console.log(this.name)return () {console.log(this.name)}},intro2: () {console.log(this.name)return function () {console.log(this.name)}}, } var obj2 {name: obj2, } obj1.intro.call(obj2)() obj1.intro().call(obj2) obj1.intro2.call(obj2)() obj1.intro2().call(obj2)obj1.intro.call(obj2)(): 第一层函数为普通函数通过call修改this为obj2打印obj2。第二层函数为箭头函数它的this与外层this相同同样打印obj2。obj1.intro().call(obj2): 第一层函数打印obj1第二次函数为箭头函数call无效它的this与外层this相同打印obj1obj1.intro2.call(obj2)(): 第一层为箭头函数call无效外层作用域为window打印window第二次为普通匿名函数默认绑定打印windowobj1.intro2().call(obj2): 与上同打印window第二层为匿名函数call修改this为obj2打印obj2 答案 obj2 obj2 obj1 obj1 window window window obj28.箭头函数扩展 总结 箭头函数没有this它的this是通过作用域链查到外层作用域的this且指向函数定义时的this而非执行时。不可以用作构造函数不能使用new命令否则会报错箭头函数没有arguments对象如果要用使用rest参数代替不可以使用yield命令因此箭头函数不能用作Generator函数。不能用call/apply/bind修改this指向但可以通过修改外层作用域的this来间接修改。箭头函数没有prototype属性。 避免使用场景 箭头函数定义对象方法 const zc {name: zc,intro: () {// this - windowconsole.log(this.name)}, } zc.intro() // undefined箭头函数不能作为构造函数 const User (name, age) {this.name namethis.age age } // Uncaught TypeError: User is not a constructor zc new User(zc, 24)事件的回调函数 DOM 中事件的回调函数中 this 已经封装指向了调用元素如果使用构造函数其 this 会指向 window 对象 document.getElementById(btn).addEventListener(click, () {console.log(this window) // true })9.综合题 学完上面的知识是不是感觉自己已经趋于化境了现在就一起来华山之巅一决高下吧。 题目 9.1: 对象综合体 var name window var user1 {name: user1,foo1: function () {console.log(this.name)},foo2: () console.log(this.name),foo3: function () {return function () {console.log(this.name)}},foo4: function () {return () {console.log(this.name)}}, } var user2 { name: user2 }user1.foo1() user1.foo1.call(user2)user1.foo2() user1.foo2.call(user2)user1.foo3()() user1.foo3.call(user2)() user1.foo3().call(user2)user1.foo4()() user1.foo4.call(user2)() user1.foo4().call(user2)这个题目并不难就是把上面很多题做了个整合如果上面都学会了此题问题不大。 user1.foo1()、user1.foo1.call(user2): 隐式绑定与显式绑定user1.foo2()、user1.foo2.call(user2): 箭头函数与 calluser1.foo3()()、user1.foo3.call(user2)()、user1.foo3().call(user2): 见题目 4.8user1.foo4()()、user1.foo4.call(user2)()、user1.foo4().call(user2): 见题目 7.5 答案: var name window var user1 {name: user1,foo1: function () {console.log(this.name)},foo2: () console.log(this.name),foo3: function () {return function () {console.log(this.name)}},foo4: function () {return () {console.log(this.name)}}, } var user2 { name: user2 }user1.foo1() // user1 user1.foo1.call(user2) // user2user1.foo2() // window user1.foo2.call(user2) // windowuser1.foo3()() // window user1.foo3.call(user2)() // window user1.foo3().call(user2) // user2user1.foo4()() // user1 user1.foo4.call(user2)() // user2 user1.foo4().call(user2) // user1题目 9.2隐式绑定丢失 var x 10 var foo {x: 20,bar: function () {var x 30console.log(this.x)}, } foo.bar() //(foo.bar)() ;(foo.bar foo.bar)() ;(foo.bar, foo.bar)()突然出现了一个代码很少的题目还乍有些不习惯。 foo.bar(): 隐式绑定打印20(foo.bar)(): 上面提到过运算符优先级的知识成员访问与函数调用优先级相同默认从左到右因此括号可有可无隐式绑定打印20(foo.bar foo.bar)()隐式绑定丢失给foo.bar起别名虽然名字没变但是foo.bar上已经跟foo无关了默认绑定打印10(foo.bar, foo.bar)(): 隐式绑定丢失起函数别名将逗号表达式的值(第二个 foo.bar)赋值给新变量之后执行新变量所指向的函数默认绑定打印10。 上面那说法有可能有几分难理解隐式绑定有个定性条件就是要满足XXX.fn()格式如果破坏了这种格式一般隐式绑定都会丢失。 题目 9.3arguments(推荐看) var length 10 function fn() {console.log(this.length) }var obj {length: 5,method: function (fn) {fn()arguments[0]()}, }obj.method(fn, 1)这个题要注意一下有坑。 fn(): 默认绑定打印 10 arguments[0](): 这种执行方式看起来就怪怪的咱们把它展开来看看 arguments: {0: fn,1: 1,length: 2 }到这里大家应该就懂了隐式绑定fn函数this指向arguments打印 2 arguments[0]: 这是访问对象的属性 00 不好理解咱们把它稍微一换方便一下理解 arguments是一个类数组arguments展开应该是下面这样 arguments: {0: fn,1: 1,length: 2 }题目 9.4压轴题(推荐看) var number 5 var obj {number: 3,fn: (function () {var numberthis.number * 2number number * 2number 3return function () {var num this.numberthis.number * 2console.log(num)number * 3console.log(number)}})(), } var myFun obj.fn myFun.call(null) obj.fn() console.log(window.number)fn.call(null) 或者 fn.call(undefined) 都相当于fn()obj.fn为立即执行函数: 默认绑定this指向window 我们来一句一句的分析 此时的 obj 可以类似的看成以下代码(注意存在闭包): obj {number: 3,fn: function () {var num this.numberthis.number * 2console.log(num)number * 3console.log(number)}, }var number: 立即执行函数的AO中添加number属性值为undefinedthis.number * 2: window.number 10number number * 2: 立即执行函数AO中number值为undefined赋值后为NaNnumber 3: AO中number值由NaN修改为3返回匿名函数形成闭包 myFun.call(null): 相当于myFun()隐式绑定丢失myFun的this指向window。 依旧一句一句的分析 var num this.number: this指向windownum window.num 10this.number * 2: window.number 20console.log(num): 打印10number * 3: 当前AO中没有number属性沿作用域链可在立即执行函数的AO中查到number属性修改其值为9console.log(number): 打印立即执行函数AO中的number打印9 obj.fn(): 隐式绑定fn的this指向obj 继续一步一步的分析 var num this.number: this-objnum obj.num 3this.number * 2: obj.number * 2 6console.log(num): 打印num值打印3number * 3: 当前AO中不存在number继续修改立即执行函数AO中的numbernumber * 3 27console.log(number): 打印27 console.log(window.number): 打印20 这里解释一下为什么myFun.call(null)执行时找不到number变量是去找立即执行函数AO中的number而不是找window.number: JavaScript 采用的静态作用域当定义函数后作用域链就已经定死。(更详细的解释文章最开始的推荐中有) 答案 10 9 3 27 20总结 默认绑定: 非严格模式下this指向全局对象严格模式下this会绑定到undefined隐式绑定: 满足XXX.fn()格式fn的this指向XXX。如果存在链式调用this 永远指向最后调用它的那个对象隐式绑定丢失起函数别名通过别名运行函数作为参数会造成隐式绑定丢失。显示绑定: 通过call/apply/bind修改this指向new绑定: 通过new来调用构造函数会生成一个新对象并且把这个新对象绑定为调用函数的this。箭头函数绑定: 箭头函数没有this它的this是通过作用域链查到外层作用域的this且指向函数定义时的this而非执行时 附赠一道面试题 var num 10 var obj { num: 20 } obj.fn (function (num) {this.num num * 3numreturn function (n) {this.num nnumconsole.log(num)} })(obj.num) var fn obj.fn fn(5) obj.fn(10) console.log(num, obj.num)
http://www.zqtcl.cn/news/650589/

相关文章:

  • 网站seo优化外包顾问网站ip解析
  • 贵阳建网站公司兼职网站推广如何做
  • 建设企业网站公司价格page做网站
  • 直播网站建设模板跨境电商选品
  • 购物网站有哪些shop++是什么
  • 自动化优化系统网站建设网站建设类文章
  • 网站建设以及推广提案书支付通道网站怎么做
  • 上海兼职做网站凤凰军事新闻
  • 青田建设局网站ui培训哪好
  • 佛山网站seo哪家好全返网站建设
  • 快速建站哪个平台好常见网页设计
  • 织梦网站地图模板网站服务费
  • 织梦建设两个网站 视频互联网公司排名1000
  • 广州企业网站设计西昌手机网
  • 一个工厂做网站有用吗wordpress重写登录页面
  • 网站服务器如何搭建网站分页设计
  • 可以直接进入网站的正能量连接温州注册网络公司
  • 清丰网站建设价格福州绿光网站建设工作室
  • 武城网站建设价格东莞容桂网站制作
  • 工作室网站需要备案吗wordpress群发工具
  • 官方网站娱乐游戏城自己做网站的好处
  • 查询建设规范的网站1元网站建设精品网站制作
  • 社交网站的优点和缺点个人网页制作软件
  • 做一家算命的网站有没有专门做淘宝客的网站
  • 网站站点管理在哪里建筑施工图设计
  • 众筹网站开发周期网页云原神
  • 哪些网站可以免费做h5东莞制作企业网站
  • 帝国cms 网站地址设置深圳住房和建设部网站
  • 专业网站建设价格最优网页游戏大全电脑版在线玩
  • 建设租车网站wordpress+js插件开发