1.箭头函数不适用的场合
由于箭头函数使得this从“动态”变成“静态”,下面两个场合不应该使用箭头函数。
第一个场合是定义对象的方法,且该方法内部包括this。
const cat = {
lives: 9,
jumps: () => {
this.lives--;
}}
上面代码中,cat.jumps()方法是一个箭头函数,这是错误的。调用cat.jumps()时,如果是普通函数,该方法内部的this指向cat;如果写成上面那样的箭头函数,使得this指向全局对象,因此不会得到预期结果。这是因为对象不构成单独的作用域,导致jumps箭头函数定义时的作用域就是全局作用域。
**第二个场合是需要动态this的时候,也不应使用箭头函数。
var button = document.getElementById('press');
button.addEventListener('click', () => {
this.classList.toggle('on');});
上面代码运行时,点击按钮会报错,因为button的监听函数是一个箭头函数,导致里面的this就是全局对象。如果改成普通函数,this就会动态指向被点击的按钮对象。
另外,如果函数体很复杂,有许多行,或者函数内部有大量的读写操作,不单纯是为了计算值,这时也不应该使用箭头函数,而是要使用普通函数,这样可以提高代码可读性。
2.注意点
- 当 var s = function g(){}时,这是一个函数表达式,不是函数声明,g只能在函数内部访问,g是只读的。
- es6对象中简写的函数体不能被new,es6中的箭头函数也不能被new。
- 使用箭头函数返回对象时必须加一对括号,如:()=> ({a:1,b:2})。
- es6中函数参数可以赋值默认值,如: ```javascript 1.function fn (x=1,y){ console.log(x,y) } fn() // 1,undefind 2.function fn1(a,b=1,c){ console.log(a,b,c); } fn1(2,,3) // 报错,不可以省略参数跳到后面的参数 fn1(2,undefind,3)// 2,1,3 如果要省略参数,只能写undefined来省略
参数变量是默认声明的,所以不能用let或const再次声明。
let x = 99; function foo(p = x + 1) { console.log(p); }
foo() // 100 x = 100; foo() // 101 默认值可以从外界拿取
- 对象和立即执行函数不能挨着写,如果对象后面写立即执行函数,对象末尾必须带“;”,否则会报错。
<a name="3HWaY"></a>
## 3.训练题
<a name="bhwC2"></a>
### 1.if代码块问题
```javascript
function a(){
console.log(1);
}
(function(){
if(false){
function a(){
console.log(2);
}
}
a();
})()
答案:
1.在ie10及ie10以下 输出2 因为在较老版本的浏览器中,无论条件是否为真,if(false)代码块中的函数声明整体提升,所以a函数执行后输出 2.
2.在现在的浏览器中会报错 Error:a is not a function,同样,无论条件是否为真,函数声明还是被提升了,但是浏览器更新后,在不符合条件的判断语句中,函数并不是整体进行提升,而只是被声明成了一个变量 即 var a;
2.手写深拷贝
function deepClone(target,origin){
var target = target || {};
var arr = '[object Array]';
for(var prop in origin){
var key = origin[prop];
if(typeof key !== "object" && typeof key !== null){
target[prop] = key;
}else{
if(Object.prototype.toString.call(key) == arr){
target[prop] = [];
}
deepClone(target[prop],key);
}
}
return target;
}
3.写出程序输出的值
this.a = 20;
var test = {
a:40,
init:() => {
console.log(this.a);
function go() {
this.a = 60;
console.log(this.a);
}
go.prototype.a = 50;
return go;
}
};
var p = test.init();
p();
new(test.init())();
答案:
20 60 60 60,在p被执行时,go函数中的this指向window,把原本的window.a的值修改成了60,所以第三次输出60.
4.手写bind方法
Function.prototype.mybind = function(other){
var self = this;
// 判断调用bind方法的是否是一个函数
if(typeof self !== "function"){
throw new Error("这不是一个函数,不能调用bind方法");
}
var slice = Array.prototype.slice,
ProFn = function() {},
thisArg = slice.call(arguments,1),
bound = function(){
var thatArg = slice.call(arguments);
return self.apply(this instanceof bound ? this : other,thisArg.concat(thatArg));
};
if(self.prototype){
ProFn.prototype = self.prototype;
}
bound.prototype = new ProFn(); //防止篡改父级的原型
return bound;
}
5.手写call
Function.prototype.myCall = function(thisArg, ...args) {
if(typeof this !== 'function') {
throw new TypeError('error')
}
const fn = Symbol('fn') // 声明一个独有的Symbol属性, 防止fn覆盖已有属性
thisArg = thisArg || window // 若没有传入this, 默认绑定window对象
thisArg[fn] = this // this指向调用call的对象,即我们要改变this指向的函数
const result = thisArg[fn](...args) // 执行当前函数
delete thisArg[fn] // 删除我们声明的fn属性
return result // 返回函数执行结果
}