这篇文章要来总结一些常见的陷阱
1
// This 的观念var myName = '全域';var person = { myName: '小明', getName: function () { return this.myName; }};var getName = person.getName;console.log(getName());// 印出来的值为何?
这段程式的重点就是函式调用的地方
那我们来看看执行的结果
这边的关键点可以看到 console.log(getName());
这边是用简易呼叫的方式,所以自然他的 this 的绑定 会是 全域。
2
// bind 的其余参数var myName = '全域';var obj = { myName: '奇怪的函式', fn: function (a, b, c) { return this.myName + ',' + a + ',' + b +',' + c; }};var fnA = obj.fn;var fnB = fnA.bind(null, 0);console.log(fnB(1, 2));// console.log 会印出哪个答案// 全域, 0, 1, 2// 奇怪的函式, 0, 1, 2// null, 0, 1, 2// 奇怪的函式, 1, 2, undefined
主要的解题重点就是在于,如果在非严格模式(sloppy mode)下,利用bind/ call/ apply的this的指向带入 null 或是 undefined 的话,会自动将 this 指向全域的 window 物件。
所以当然答案是全域,而且利用 bind 的方式多传入了一个参数0,之后又再执行的时候才又传入了参数1, 2,所以顺序当然是0, 1, 2,最后的结果就是 全域, 0, 1, 2
。
3
延续上个问题喔,如果我们想要结果印出来是 null, 0, 1, 2的话该怎么半?
很简单喔~就只要让函式执行的时候进入严格模式(strict mode)就好。
// bind 的其余参数var myName = '全域';var obj = { myName: '奇怪的函式', fn: function (a, b, c) { 'use strict'; // <<<<<<<< 加入这段 return this + ',' + a + ',' + b +',' + c; // <<<<<< 移除myName的变数 }};var fnA = obj.fn;var fnB = fnA.bind(null, 0);console.log(fnB(1, 2));
4
// This 的观念var value = 'global';var foo = { value: 'local', bar: function () { return this.value; }};// 直接执行console.log(foo.bar());// 赋值console.log((foo.bar = foo.bar)());// orconsole.log((false || foo.bar)());
可以看到第一个 console.log(foo.bar());
是在 foo 的物件下执行 bar 这个 function,所以 this 的指向很明显是 local。
再来第二个,console.log((foo.bar = foo.bar)());
利用 = 赋值运算子会回传右边的内容做为回传值,在这边就是回传了右边的 foo.bar,那么这样取出来的其实就是函式本身,之后又在外层有一个括号的运算子,后面又直接执行,这样的状况视为简易呼叫的概念。
所以答案会是 global。
最后一个 or 的运算子,当左边的运算元是 falsy 的结果的时候就会回传右边的运算元内容,所以状况跟第二个一样,也是回传右边的 foo.bar 的 function,那么答案自然也是 global。
5
// Callback Functionvar arr = ['1', '2', '3'].map(parseInt);console.log(arr);// [1, 2, 3]// [undefined, undefined, undefined]// [1, NaN, NaN]// [1, undefined, undefined]
我们先来看看结果是
好,我们先分别来看看 map()
以及 parseInt()
这两个方法是甚么
map()
map 就是会依序把阵列的每个项目运算过后,并回传一个新的 arr 阵列
var arr = ['1', '2', '3'].map(function (item) { console.log(item); return 'a' + item;});
parseInt()
也就是说,parseInt() 第一个参数是要转换成数字的字串,第二个参数是要用多少的进位数,如果都不设定,预设是 10 进位。
var arr = ['1', '2', '3'].map(function (item) { console.log(item); return parseInt(item, 10);});
讲解完两个方法之后我们来解析为什么答案会是 [1, NaN, NaN]
主要的原因是 map 所带入的 callback function 中,所带入的参数不只有 item 一个
主要有
item => 当前的对象
index => 当前的对象在该阵列中的索引值
array => 这个阵列的本身
那么我们来看看原本的程式码是
// Callback Functionvar arr = ['1', '2', '3'].map(parseInt);console.log(arr);
但它其实相当于:
var arr = ['1', '2', '3'].map(function (item, index) { return parseInt(item, index);});
所以当 1 进去的时候,parseInt('1', 0); => 答案是 1
当 2 进去的时候,parseInt('2', 1); => 答案是 NaN
当 3 进去的时候,parseInt('3', 2); => 答案是 NaN
PS: 进为模式的观念其实是这样
当你是用 0 作为进位指定基数,它就会直接回传你原本的值。
当你是用 1 作为进位指定基数,它就会直接回传你NaN。
当你是用 2 作为进位指定基数,它就会依照你的0跟1字串来进行演算。
其他进位模式各位也可以试着练习看看喔,今天就先这样~希望对大家有帮助~汪汪