this、高阶函数、闭包、箭头函数、generator
1. this
函数this指向问题:
(1)this和它声明环境无关,而完全取决于他的执行环境,this指向函数调用时的对象。
(2)嵌套函数中的this不会继承上层函数的this,如果需要,可以用一个变量保存上层函数的this。
this
的简单例子
function logthis() {
return this;
}
var persion = {
name :'wangdaji',
logme: function() {}
};
persion.logme = logthis;
logthis(); // window
logthis.apply(persion);// persion
logthis.call(persion); // persion
persion.logme(); // person
this
使用call()
和apply()
来指定对象
// 全局变量
window.a = 1;
window.b = 2;
// oo对象
var oo = {
a : 1,
b : 2
};
function add(c, d) {
return this.a + this.b + c + d;
}
add(3, 4); // 10 , this is window
add.apply(oo, [3, 4]);// 10 , this is oo
add.call(oo, 3, 4); // 10 , this is oo
this
的进阶例子
// p1对象
var p1 = {
name: 'wangdaji',
eat: function() {
console.log(this.name + ' eatting');
},
run: function() {
var toRun = function() {
console.log(this.name + ' running');
}
toRun();
}
};
// p2对象
var p2 = {
name: 'xiaoming',
eat: function() {
console.log(this.name + ' eatting');
},
run: function() {
var that = this;
var toRun = function() {
console.log(that.name + ' running');
}
toRun();
}
};
console.log(p1.eat()); // wangdaji eatting
console.log(p1.run()); // undefined running
console.log(p2.eat()); // xiaoming eatting
console.log(p2.run()); // xiaoming running
// ---注意点---
p1.eat = p2.eat;
p1.run = p2.run;
console.log(p1.eat()); // wangdaji eatting
console.log(p1.run()); // wangdaji running
// ---注意点---
var action = function () {};
action = p2.run;
console.log(action.apply(p1)); // wangdaji running
this
本质
参考:http://www.ruanyifeng.com/blog/2018/06/javascript-this.html
变量在内存中的数据结构:
var obj = { foo: 5 };

函数在内存中的数据结构:
var obj = { foo: function () {} };

this在内存数据结构中的理解:
由于函数可以在不同的运行环境执行,所以需要有一种机制,能够在函数体内部获得当前的运行环境(context)。所以,this就出现了,它的设计目的就是在函数体内部,指代函数当前的运行环境。
var f = function () {
console.log(this.x);
}
var x = 1;
var obj = {
f: f,
x: 2,
};
执行f()
:函数f在全局环境执行,this.x指向全局环境的x。

执行obj.f()
:在obj环境执行,this.x指向obj.x。

2. 高阶函数
JavaScript的函数其实都指向某个变量。变量可以指向函数,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。
实现:[1, 2, 3, 4] ==> [1, 4, 9, 16]
var arr = [1, 2, 3, 4];
function square(x) {
return x * x;
}
function mapp(f) {
var arrr = new Array();
this.forEach(function(e, i, arr) {
arrr.push(f(e));
});
return arrr;
}
arr.mapp = mapp;
// 最终调用
var squareArr = arr.mapp(square);
console.log(squareArr);
数组的map
、reduce
、filter
、sort
方法:
map
:传入一个函数,把传入的函数依次作用于每个元素,然后根据返回值组成的数组返回。
实现:[1, -2, 3, -4] ==> [1, 2, 3, 4]
var arr = [1, -2, 3, -4];
var results = arr.map(Math.abs);
console.log(results);
reduce
:传进一个函数,此函数接受两个参数,把结果继续和序列的下一个元素做累积计算。
实现:[1, 2, 3, 4] ==> 1234
var arr = [1, 2, 3, 4];
var results = arr.reduce(function (x, y) {
return x * 10 + y;
});
console.log(a);
filter
:传入一个函数,把传入的函数依次作用于每个元素,然后根据返回值是true还是false决定保留还是丢弃该元素。
// 将数组去重
var results,
arr = ['apple', 'strawberry', 'banana', 'pear', 'apple', 'orange', 'orange', 'strawberry'];
results = arr.filter(function (element, index, self) {
return self.indexOf(element) === index;
})
console.log(results.toString());
sort()
:用于排序的,Array的sort()
方法默认把所有元素先转换为String再排序。sort()
是一个高阶函数,可以传入比较规则。
js中因为Array的sort()方法默认把所有元素先转换为String再排序,所以不能将数字直接进行排序。
3. 闭包
变量的作用域:父对象的的所有变量对子对象是可见的,反之不可以。
闭包是函数的指针,使用闭包可以访问函数内部的局部变量,而且局部变量的值可以一直保持在内存中。
acc和acc1就是闭包,但两者不相等,是不同的对象,并且对应函数内部的变量也是不同的。
// 初始值 + 数字数组每一元素 的和
function account(initial) {
var sum = 0 + initial;
return function (...rest) {
rest.forEach(function(e, i, a) {
sum += e;
});
return sum;
}
}
// acc父函数,result子函数,不管result执行多少次,acc中的sum参数会一直存在
var acc = account(10);
var result = acc(1, 1, 1);
console.log(result);
var result = acc(2, 2, 2);
console.log(result);
闭包使用的注意点:
(1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大。(2)如果子函数是公用方法,直接使用子函数可能会修改到父函数中的私有变量,那么会导致数据的异常。
4. 箭头函数
练习:
// 一个参数
var pow2 = function (x) {
return x * x;
}
console.log(pow2(11));
var fn = x => x * x;
console.log(fn(11));
// 没有参数
var pi = () => 3.14
console.log(pi());
// 多个参数 和 函数实现
var mylog = (x, y, ...rest) => {
console.log(x);
console.log(y);
rest.forEach(function(e, i, a) {
console.log(e);
});
}
mylog(1, 10, 2, 30, 59);
箭头函数完全修复了this的指向,this总是指向词法作用域,也就是外层调用者obj。
// 没有使用箭头函数
var p1 = {
name: 'xiaoming',
run: function () {
that = this;
var fn = function () {
console.log(that.name + ' runing');
}
return fn();
}
};
p1.run();
// 使用箭头函数
var p = {
name: 'wangfaji',
run: function () {
var fn = () => console.log(this.name + ' runing');
return fn();
}
};
p.run();
此处的this指向的是p对象。
5. generator
generator(生成器)是ES6标准引入的新的数据类型。类似函数的定义方式,可以简单理解成可以返回多次的函数
。
generator定义:function*
来定义。用yield
返回多次。
一个id自增的generator。
function *nextid(a) {
let x = 0;
while(x < a) {
yield ++x;
}
yield 12;
yield 13;
}
// 只是一个generator对象,无法执行
var next = nextid(5);
console.log(next.next());// Object { value: 1, done: false }
console.log(next.next());// Object { value: 2, done: false }
console.log(next.next());// Object { value: 3, done: false }
console.log(next.next());// Object { value: 4, done: false }
console.log(next.next());// Object { value: 5, done: false }
console.log(next.next());// Object { value: 12, done: false }
console.log(next.next());// Object { value: 13, done: false }
console.log(next.next());// Object { value: undefined, done: true }
next()
方法会返回generator的代码的下一个yield
后面的值。
for of
,则不需要判断done的参数,并直接返回value值。
for (let value of nextid(5)) {
console.log(value);
}
generator还有另一个巨大的好处,就是把异步回调代码变成“同步”代码。
网友评论