注意:本篇学习笔记基于原网站: JavaScript教程 - 廖雪峰的官方网站
笔记仅作学习留档使用
本篇目录
闭包
箭头函数(ES6)
标签函数
生成器(ES6)
闭包
在函数内部写函数,将内部函数作为结果值返回。
- 调用原函数返回内部函数,调用内部函数返回函数过程结果。
- 每次调用原函数都会返回一个新的函数,即使传入相同的参数,这些新函数的调用结果互不影响。
function lazy_sum(arr) {let sum = function () {return arr.reduce(function (x, y) {return x + y;});}return sum;
}let f = lazy_sum([1, 2, 3, 4, 5]); // function sum()
f(); // 15
let f1 = lazy_sum([1, 2, 3, 4, 5]);
f1 === f; // false
返回的函数并没有立刻执行,而是直到调用了f()才执行,所以返回函数不要引用任何循环变量,或者后续会发生变化的变量,要不然会这样:
function count() {let arr = [];for (var i=1; i<=3; i++) {//这里是var定义arr.push(function () {return i * i;});}return arr;
}let results = count();
let [f1, f2, f3] = results;
f1(); // 16
f2(); // 16
f3(); // 16
非要用那可以这么写:
/*1.创建一个函数,用该函数的参数绑定循环变量当前的值*/
function count() {let arr = [];for (var i=1; i<=3; i++) {arr.push((function (n) { // n绑定循环变量当前的值return function () {return n * n;}})(i));}return arr;
}let [f1, f2, f3] = count();f1(); // 1
f2(); // 4
f3(); // 9/*2.或者把循环变量i用let定义在for循环体中*/
function count() {let arr = [];for (let i=1; i<=3; i++) {arr.push(function () {return i * i;});}return arr;
}
(function (x) { return x * x }) (3);:这种写法的意思是“创建一个匿名函数并立刻执行”
使用实例:计数器
function create_counter(initial) {let x = initial || 0;return {inc: function () {x += 1;return x;}}
}let c1 = create_counter();
c1.inc(); // 1
c1.inc(); // 2
c1.inc(); // 3let c2 = create_counter(10);
c2.inc(); // 11
c2.inc(); // 12
c2.inc(); // 13
let x = initial || 0:将变量initial赋值给x,但如果initial是假值(falsy),则使用0作为默认值。- 常见假值:
undefined、null、false、0、NaN、""(空字符串)
箭头函数(ES6)
在高阶函数(函数笔记上篇)里用过一次,相当于匿名函数
x => x * x
//等价于↓
function (x) {return x * x;
}/*多条语句的情况*/
x => {if (x > 0) {return x * x;}else {return - x * x;}
}/*参数数量不为1的情况*/
(x, y) => x * x + y * y// 两个参数
() => 3.14// 无参数
// 可变参数
(x, y, ...rest) => {let i, sum = x + y;for (i=0; i<rest.length; i++) {sum += rest[i];}return sum;
}
如果要返回对象要加():
// SyntaxError:
x => { foo: x }
// ok:
x => ({ foo: x })
this
箭头函数内部的this比较特殊,是词法作用域,由上下文确定。用上篇里的例子:
let obj = {birth: 1990,getAge: function () {let b = this.birth; // 1990let fn = function () {return new Date().getFullYear() - this.birth; // this指向window或undefined};return fn();}
};//用this可以解决
//箭头函数定义在 getAge方法内部,而 getAge方法的 this指向调用它的对象 obj
let obj = {birth: 1990,getAge: function () {let b = this.birth; // 1990let fn = () => new Date().getFullYear() - this.birth; // this指向obj对象return fn();}
};
obj.getAge(); // 25
由于this在箭头函数中按照词法作用域是绑定的,所以用call()或者apply()无法对this进行绑定,传入的第一个参数会被忽略
let obj = {birth: 1990,getAge: function (year) {let b = this.birth; // 1990let fn = (y) => y - this.birth; // this.birth仍是1990return fn.call({birth:2000}, year);}
};
obj.getAge(2015); // 25
标签函数
用一个模拟sql数据查询解释
//变量声明
const email = "test@example.com";
const password = 'hello123';
//定义函数
function sql(strings, ...exps) {console.log(`SQL: ${strings.join('?')}`);console.log(`SQL parameters: ${JSON.stringify(exps)}`);return {name: '小明',age: 20};
}
//调用
const result = sql`SELECT * FROM users WHERE email=${email} AND password=${password}`;
//打印
console.log(JSON.stringify(result));
原博这里说的比较简略,我详细解释一下可能有点难懂的部分:
function sql(strings, ...exps) {
strings:接收模板字符串的所有静态部分
这里strings=["SELECT * FROM users WHERE email=", " AND password=", ""]
注意:这里在第二个插值${password}之后,虽然没有静态文本,但仍然有一个位置存在,所以strings最后接收到一个"",如果输入语句在最后一个动态插值后没有别的字符就会这样。...exps:接收模板字符串中的所有动态插值(${...}部分)
这里exps=["test@example.com", "hello123"]
console.log(`SQL: ${strings.join('?')}`);
这里将 strings数组用 ?连接起来,是模拟SQL参数化查询,?是数据库用的参数占位符,表示我还不知道这里要放什么数据,先用这个符号占一下位置。
打印显示为:SQL: SELECT * FROM users WHERE email=? AND password=?
console.log(`SQL parameters: ${JSON.stringify(exps)}`);
打印所有参数值,JSON.stringify(exps)函数作用是将参数数组转换为 JSON 字符串。
打印结果会显示为:SQL parameters: ["test@example.com","hello123"]
const result = sql`SELECT * FROM users WHERE email=${email} AND password=${password}`;
这就是标签模板语法的调用方式,反引号 ```包裹的是模板字串,${email}和 ${password}是变量插值,整个结构被传递给 sql函数,普通调用的话其实应该是sql(["SELECT * FROM users WHERE email=", " AND password=", ""], email, password)
console.log(JSON.stringify(result));
将函数返回的对象转换为 JSON 字符串
输出:{"name":"小明","age":20}
这样调用的时候会简单很多
let id = 123;
let age = 21;
let score = 'A';update`UPDATE users SET age=${age}, score=${score} WHERE id=${id}`;
生成器(ES6)
看上去像一个函数,但可以返回多次,借鉴了Python的generator的概念和语法。由function*定义(注意多出的*号),除了return语句,还可以用yield返回多次。可以实现需要用面向对象才能实现的功能。
function* foo(x) {yield x + 1;yield x + 2;return x + 3;
}
以斐波那契数列的函数当例子:
function* fib(max) {lett,a = 0,b = 1,n = 0;while (n < max) {yield a;[a, b] = [b, a + b];n ++;}return;
}
//直接调用不行
fib(5); // fib {[[GeneratorStatus]]: "suspended", [[GeneratorReceiver]]: Window}/*调用方法*/
//方法一:不断地调用generator对象的next()
let f = fib(5);
f.next(); // {value: 0, done: false}
f.next(); // {value: 1, done: false}
f.next(); // {value: 1, done: false}
f.next(); // {value: 2, done: false}
f.next(); // {value: 3, done: false}
f.next(); // {value: undefined, done: true}//方法二:直接用for ... of循环迭代generator对象
function* fib(max) {leta = 0,b = 1,n = 0;while (n < max) {yield a;[a, b] = [b, a + b];n ++;}return;
}for (let x of fib(10)) {console.log(x); // 依次输出0, 1, 1, 2, 3, ...
}