1.定义函数的两种方法:
function abs(x) { if (x >= 0) { return x; } else { return -x; }}
或者
var abs = function (x) { if (x >= 0) { return x; } else { return -x; }}; //记得要加分号,因为这是赋值abs变量
2.调用函数
abs(10, 'blablabla'); // 返回10abs(-9, 10, 'hehe', null); // 返回9,不受多个参数的影响,因为第一个参数就return了abs(); // x为undefined,返回NaN
3.关键字arguments
,类似于array.
function foo(x) { alert(x); // 10 for (var i=0; i
arguments
最常用于判断传入参数的个数:
if (arguments.length === 0) //如果参数个数为0...
4.用rest获取所有参数:
function foo(a, b) { var i, rest = []; if (arguments.length > 2) { for (i = 2; i
一般写成如下更加简洁明了,与上面等效:
function foo(a, b, ...rest) { console.log('a = ' + a); console.log('b = ' + b); console.log(rest);}foo(1, 2, 3, 4, 5);// 结果:// a = 1// b = 2// Array [ 3, 4, 5 ]foo(1);// 结果:// a = 1// b = undefined// Array []
注意rest前面有3个点,如果没有的话,相当于rest就是第三个参数:
function foo(a, b,rest) { console.log('a = ' + a); console.log('b = ' + b); console.log(rest);}foo(1, 2, 3, 4, 5);// 结果:// a = 1// b = 2// 3foo(1);// 结果:// a = 1// b = undefined// undefined
5.接受任意个参数并返回它们的和:
function sum(...rest) { var sum = 0; for(var i of rest) { sum += i; } return sum;}
6.JavaScript在行末自动添加分号
7.不同函数内部的同名变量互相独立,互不影响
8.内部函数可以访问外部函数定义的变量,反过来则不行:
function foo() { var x = 1; function bar() { var y = x + 1; // bar可以访问foo的变量x! } var z = y + 1; // ReferenceError! foo不可以访问bar的变量y!}
9.JavaScript会先扫描整个函数体的语句,把所有申明的变量“提升”到函数顶部,但不会提升变量的赋值:
'use strict';function foo() { var x = 'Hello, ' + y; alert(x); var y = 'Bob';}foo(); //结果显示Hello, undefined,说明y没有值,但是已经声明了
10.JavaScript默认有一个全局对象window
,全局作用域的变量实际上被绑定到window
的一个属性:
'use strict';var course = 'Learn JavaScript';alert(course); // 'Learn JavaScript'alert(window.course); // 'Learn JavaScript' 与上面等效
11.let解决块级作用域
'use strict';function foo() { for (var i=0; i<100; i++) { // } i += 100; // 仍然可以引用变量i}
'use strict';function foo() { var sum = 0; for (let i=0; i<100; i++) { sum += i; } i += 1; // SyntaxError,无法引用i}
12.关键字const
来定义常量,const
与let
都具有块级作用域:
'use strict';const PI = 3.14;PI = 3; // 某些浏览器不报错,但是无效果!PI; // 3.14
13.this指的是被调用函数的当前对象 ,多层内嵌会使指示错误,可以在函数开头用var that = this;来捕捉固定this
要指定函数的this
指向哪个对象,可以用函数本身的apply
方法,它接收两个参数,第一个参数就是需要绑定的this
变量,第二个参数是Array
,表示函数本身的参数。
function getAge() { var y = new Date().getFullYear(); return y - this.birth;}var xiaoming = { name: '小明', birth: 1990, age: getAge};xiaoming.age(); // 25getAge.apply(xiaoming, []); // 25, this指向xiaoming, 参数为空
对普通函数调用,我们通常把this
绑定为null
。
另一个与apply()
类似的方法是call()
,唯一区别是:
-
apply()
把参数打包成Array
再传入; -
call()
把参数直接按顺序传入。
14.map函数用法,()里调用其他函数
function pow(x) { return x * x;}var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];arr.map(pow); // [1, 4, 9, 16, 25, 36, 49, 64, 81] 令数组的所有元素都执行()里的函数
还能这样
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];arr.map(String); // ['1', '2', '3', '4', '5', '6', '7', '8', '9']
15.reduce函数用法:必须接收两个参数,reduce()
把结果继续和序列的下一个元素做累积计算,例如求和:
var arr = [1, 3, 5, 7, 9];arr.reduce(function (x, y) { return x + y;}); // 25
其他例子
var arr = [1, 3, 5, 7, 9];arr.reduce(function (x, y) { return x * 10 + y; // 把return值当成下一个x,与下一个y循环函数}); // 13579
16.箭头函数
function (x) { return x * x;} //相当于箭头函数x => x * x
如果包含多条语句,这时候就不能省略{ ... }
和return
如果参数不是一个,就需要用括号()
括起来:
// 两个参数:(x, y) => x * x + y * y// 无参数:() => 3.14// 可变参数:(x, y, ...rest) => { var i, sum = x + y; for (i=0; i
如果要返回一个对象(单表达式),要外加一个括号,写成:
x => ({ foo: x })
回顾前面的例子,
var obj = { birth: 1990, getAge: function () { var b = this.birth; // 1990 var fn = () => new Date().getFullYear() - this.birth; // this指向obj对象 return fn(); }};obj.getAge(); // 25
箭头函数使this
总是指向词法作用域,也就是外层调用者obj,如果是之前的直接创建函数,this就会指向window
由于this
在箭头函数中已经按照词法作用域绑定了,所以,用call()
或者apply()
调用箭头函数时,无法对this
进行绑定,即传入的第一个参数被忽略:
var obj = { birth: 1990, getAge: function (year) { var b = this.birth; // 1990 var fn = (y) => y - this.birth; // this.birth仍是1990 return fn.call({birth:2000}, year); //箭头函数里的this已经绑定原对象obj, // 此时的this.birth仍然是1990, // 而不会将this绑定到传入的 birth:2000 }};obj.getAge(2015); // 25
17.split() 方法用于把一个字符串分割成字符串数组,如果要把字符串数组变成“数”组,要乘以1,可以如下写
var arr=string.split('').map(x => x * 1)
18.parseInt()函数可解析一个字符串,并返回一个整数。
语法:parseInt(string, radix);
string 必需。要被解析的字符串。
radix 可选。表示要解析的数字的基数。该值介于 2 ~ 36 之间。
如果省略该参数或其值为 0,则数字将以 10 为基础来解析。
如果它以 “0x” 或 “0X” 开头,将以 16 为基数。
如果该参数小于 2 或者大于 36,则 parseInt() 将返回 NaN。
parseInt("10"); //返回 10 10进制parseInt("19",10); //返回 19 (10+9) 10进制parseInt("11",2); //返回 3 (2+1) 2进制parseInt("17",8); //返回 15 (8+7) 8进制parseInt("1f",16); //返回 31 (16+15) 16进制parseInt("010"); //未定:返回 10 或 8
19.filter方法
var arr = [1, 2, 4, 5, 6, 9, 10, 15];var r = arr.filter(function (x) { return x % 2 !== 0; //把arr中的偶数过滤掉,留下x%2!==0的});r; // [1, 5, 9, 15]
var arr = ['A', '', 'B', null, undefined, 'C', ' '];var r = arr.filter(function (x) { return x && x.trim(); // 把false的空字符串过滤掉}); //注意:IE9以下的版本没有trim()方法arr; // ['A', 'B', 'C']
var arr = ['A', 'B', 'C'];var r = arr.filter(function (element, index, self) { // 可以输入3个参数 console.log(element); // 依次打印'A', 'B', 'C' console.log(index); // 依次打印0, 1, 2 console.log(self); // self就是变量arr 会输出3次Array [ "A", "B", "C" ] return true;});
var r = arr.filter(function (element, index, self) { return self.indexOf(element) === index;}); //过滤掉arr中重复的元素 indexOf() 方法返回某个指定的字符串值在字符串中首次出现的位置,注意是首次
选出数组里面的素数:
function get_primes(arr) { var i; var res = arr.filter(function(x){ if(x<2) return false; //排除掉1 for(i=2; i*i<=x; i++){ if(x%i === 0){ return false;} //从2开始往上面的数一个个地除,直到确认是不是素数 } return true; //是素数就return true。 }); return res;}
20.对于两个元素x
和y
,如果认为x < y
,则返回-1
,如果认为x == y
,则返回0
,如果认为x > y
,则返回1
21.闭包(不大懂意义):返回一个函数,不立即执行
function lazy_sum(arr) { var sum = function () { return arr.reduce(function (x, y) { return x + y; }); } return sum;}var f = lazy_sum([1, 2, 3, 4, 5]); // function sum() //调用lazy_sum()时,返回的并不是求和结果,而是返回求和函数f(); // 15 //调用函数f时,才是真正计算求和的结果
当我们调用lazy_sum()
时,每次调用都会返回一个新的函数,即使传入相同的参数(每次调用的结果互不影响):
var f1 = lazy_sum([1, 2, 3, 4, 5]);var f2 = lazy_sum([1, 2, 3, 4, 5]);f1 === f2; // false
返回的函数并没有立刻执行,而是直到调用了f()
才执行:
function count() { var arr = []; for (var i=1; i<=3; i++) { // 这里正常思维是最终i=4,但是没想到4最后居然进入循环里面运算了??最终return 4*4 arr.push(function () { return i * i; }); } return arr;}var results = count();var f1 = results[0];var f2 = results[1];var f3 = results[2];f1(); // 16f2(); // 16f3(); // 16 等到3个函数都返回时,它们所引用的变量i已经变成了4
正确写法:
function count() { var arr = []; for (var i=1; i<=3; i++) { arr.push((function (n) { // 在里面再创建一个函数,用该函数的参数绑定循环变量当前的值 return function () { return n * n; } })(i)); // 这样是用i去立即执行函数 注意括号的括法,(function (x) { return x * x }) (i); } return arr;}var results = count();var f1 = results[0];var f2 = results[1];var f3 = results[2];f1(); // 1f2(); // 4f3(); // 9
借助闭包,可以封装一个私有变量。例如用JavaScript创建一个计数器:
'use strict';function create_counter(initial) { var x = initial || 0; //在没有初始值initial的时候给一个初始值 return { inc: function () { x += 1; return x; } }}var c1 = create_counter();c1.inc(); // 1c1.inc(); // 2c1.inc(); // 3var c2 = create_counter(10);c2.inc(); // 11c2.inc(); // 12c2.inc(); // 13
换句话说,闭包就是携带状态的函数,并且它的状态可以完全对外隐藏起来:
function make_pow(n) { return function (x) { return Math.pow(x, n); // 计算x的n次方 }}// 创建两个新函数:var pow2 = make_pow(2);var pow3 = make_pow(3);pow2(5); // 25 相当于Math.pow(5, 2)pow3(7); // 343 相当于Math.pow(7, 3)
只需要用函数,就可以用计算机实现运算,而不需要0
、1
、2
、3
这些数字和+
、-
、*
、/
这些符号:
'use strict';// 定义数字0:var zero = function (f) { return function (x) { return x; }};// 定义数字1:var one = function (f) { return function (x) { return f(x); }};// 定义加法:function add(n, m) { return function (f) { return function (x) { return m(f)(n(f)(x)); } }}// 计算数字2 = 1 + 1:var two = add(one, one);// 计算数字3 = 1 + 2:var three = add(one, two);// 计算数字5 = 2 + 3:var five = add(two, three);// 给3传一个函数,会打印3次:(three(function () { console.log('print 3 times');}))();// 给5传一个函数,会打印5次:(five(function () { console.log('print 5 times');}))();
22.(意义不明啊!)generator(生成器)是ES6标准引入的新的数据类型。一个generator看上去像一个函数,但可以返回多次。
编写一个产生斐波那契数列的函数:
function* fib(max) { //注意多出的*号 var t, a = 0, b = 1, n = 1; while (n < max) { yield a; t = a + b; a = b; b = t; n ++; } return a;}fib(5); // fib {[[GeneratorStatus]]: "suspended", [[GeneratorReceiver]]: Window} // fib(5)仅仅是创建了一个generator对象,还没有去执行它 //可以这样调用:var 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: true}
next()方法每次遇到yield x;
就返回一个对象{value: x, done: true/false}
,然后“暂停”。返回的value
就是yield
的返回值,done
表示这个generator是否已经执行结束了。
一般这样调用:
for (var x of fib(6)) { // 注意要6,坑啊 console.log(x); // 依次输出0, 1, 1, 2, 3}
下面是天书:
用generator的话,AJAX可以大大简化代码:
try { r1 = yield ajax('http://url-1', data1); r2 = yield ajax('http://url-2', data2); r3 = yield ajax('http://url-3', data3); success(r3);}catch (err) { handle(err);}
用一个对象来保存状态:
var fib = { a: 0, b: 1, n: 0, max: 5, next: function () { var r = this.a, t = this.a + this.b; this.a = this.b; this.b = t; if (this.n < this.max) { this.n ++; return r; } else { return undefined; } }};
自增,并保存数字:
'use strict';function* next_id() {var i=1;while(true){ yield i++;}}