var
不能声明块级的变量,js的函数内变量声明会被提高至函数体开头let
则用来解决这个块级变量声明,于ES6引入。const
用于声明常量,也是ES6引入。javascript
全局变量会被默认绑定到window
,不一样JS文件若是定义了相同的名称的全局变量或者顶级函数,那么就会致使冲突。所以,解决方法就是把本身的全局变量绑定到一个全局变量中,相似于Java中的class,我的感受。java
var xiaoming = { name: '小明', birth: 1990, age: function () { var y = new Date().getFullYear(); return y - this.birth; } }; xiaoming.age; // function xiaoming.age() xiaoming.age(); // 今年调用是25,明年调用就变成26了
由于xiaoming是个对象,因此方法内部的this就是指当前xiaoming这个对象,和java对象内部使用的this是一个意思。可是若是在方法内不又定义了方法,并在这个内部方法里引用this,那么这个this将报错,undefined或者window。若是从java角度看的话,有点像是内部类里使用this于是出错。 但也能够在内部方法的外层,捕获this对象,好比赋值给that。算法
每一个函数都自带apply
和call
方法,其中apply
接受两个参数(object, arguments[])
, 即传入对象自己和参数数组,这样就方法体内的this
就会自动指向object
,从而避免this乱指。数组
call
方法与apply
具备一样的功能,第一个也是object
本身,以后是参数列表,非Array
。
普通函数通常将obejct赋值为null
。
好比闭包
Math.max.call(null, 3,4,4,5,6,6,67,8,9); Math.max.apply(null, [3,4,4,5,6,6,67,8,9]);
javascript全部的对象都是动态,即便是内置的函数,也能够从新指向新的函数。
如window上有个全局函数parseInt()函数,须要统计parseInt被调用次数。app
var count = 0; var oldParseInt = parseInt; // 保存原函数 window.parseInt = function () { count += 1; // arguments是函数的参数数组 return oldParseInt.apply(null, arguments); // 调用原函数 }; // 测试: parseInt('10'); parseInt('20'); parseInt('30'); count; // 3
能够看出来JavaScript如此轻易就实现了装饰器,java哭晕在厕所。函数
定义:高阶函数英文叫Higher-order function。
JavaScript的函数其实都指向某个变量。既然变量能够指向函数,函数的参数能接收变量,那么一个函数就能够接收另外一个函数做为参数,这种函数就称之为高阶函数。这个厉害了,WOW。
里面也涉及到一个javascript的函数的参数是能够传入多个或不传的原理,因此方法体内才能够直接对传入变量输入参数,而且个数不限制。直接show me the fucking source code。学习
function add(x, y, f){ return f(x) + f(y); } var f = Math.abs; add(6, -3, f); // 输出9
666啊,这个map/reduce不是大数据用的么?竟然是js的高阶函数啊,看起来好吊啊,真是吊打java了。
网上找了一段解释:测试
array.map(callback[, thisArg]);
map 方法会给原数组中的每一个元素都按顺序调用一次 callback 函数。callback 每次执行后的返回值组合起来造成一个新数组。 callback 函数只会在有值的索引上被调用;那些历来没被赋过值或者使用 delete 删除的索引则不会被调用。大数据
callback 函数会被自动传入三个参数:数组元素,元素索引,原数组自己。
若是 thisArg 参数有值,则每次 callback 函数被调用的时候,this 都会指向 thisArg 参数上的这个对象。若是省略了 thisArg 参数,或者赋值为 null 或 undefined,则 this 指向全局对象 。
如对每一个元素进行平方运算。
var pow = function(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]
。
因此结合上面的解释就是map对数组arr的每一个元素调用一次callback即pow()函数。因为map会给callback传入三个参数,而pow只取了第一个参数,即数组元素,因此结果是对每一个元素平方,而后组成成新的数组。
咱们能够试着修改pow,去把传入到callback的第二个参数也使用上,那么咱们来试试给给每一个元素平方后加上他的索引值。
var pow = function(x){ return x*x+arguments[1]; } var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9]; arr.map(pow);
输出结果为[1, 5, 11, 19, 29, 41, 55, 71, 89]
。
更为具体的map定义能够看MSDN的解释。
接下来看看reduce函数。一样做为array的方法,reduce是对数组的每个元素与其下一个元素调用callback一次方法,并将callback的结果做为下一次的参数与下一个元素再次调用callback方法,进行累积运算。有点绕,看数学公式可能更好理解
[x1, x2, x3, x4].reduce(f) = f(f(f(x1, x2), x3), x4)
因此咱们来进行一个求和,计算1到100和运算。
var arr = []; for(var i = 1; i <= 100; i++){ arr[i-1] = i; } arr.reduce(function (x, y){ return x+y; });
输出结果为5050
。
再来看看具体reduce定义
arr.reduce(callback[,initialValue])
initialValue做为reduce方法的可选参数,callback接收4个参数, accumulator 是累积值,若是reduce方法设定了initialValue,那么accumulator的初始值就是initialValue。
accumulator
currentValue
currentIndex
array
因此咱们能够继续尝试设定初始的方式
var arr = []; for(var i = 1; i <= 100; i++){ arr[i-1] = i; } arr.reduce(function (x, y){ return x+y; }, 100);
咱们将初始值设为100,那么输出结果是5150。
测试题
不要使用JavaScript内置的parseInt()函数,利用map和reduce操做实现一个string2int()函数:
function string2ints(s){
return s.split('').map(function(x){ return x*1}).reduce(function(x, y){ return x*10+y*1; });
}
请把用户输入的不规范的英文名字,变为首字母大写,其余小写的规范名字。输入:['adam', 'LISA', 'barT']
,输出:['Adam', 'Lisa', 'Bart']
。
// comment on this var arr = ["LINda", "adam", "barT"]; arr.map(function(x){ return x.toLowerCase();}).map(function(x){ return x[0].toUpperCase()+x.slice(1); });
小明的疑惑在与没搞懂parseInt的接受的参数,由于map的callback默认是会传入三个参数,而parseInt的参数有两个parseInt(string, radix);那么此时parseInt自动忽略传入的第三个参数array,而传入了(element, index)。索引改变了进制,于是获得错误的结果。
这个感受是个坑,还不清楚IDE是否会提示函数或者方法的参数为啥。
语法:var new_array = arr.filter(callback[, thisArg])
filter也是一个经常使用的操做,它用于把Array的某些元素过滤掉,而后返回剩下的元素。与map相同的是,它也接受一个函数callback,而后对每一个元素调用函数进行处理,不一样的地方在于,函数须要返回true或false,用户决定是保留该元素仍是删除该元素,最后生成一个新的数组,如其名字所决定的同样。thisAgrs不用解释。
其中callback一样接收3个参数element,index,array。
简单的作个去重操做和过滤掉奇数。
var arr = [1,2,3,4,4,4,5,5,5,6,6,7,7,8,8,9,9,9]; arr.filter(function(x){return x % 2 === 0;}) .filter(function(element, index, self){ return self.indexOf(element) === index;} );
self.index(element)
只会返回找到的第一个元素的index,所以后续的index就再也不相等了。
在作一个找出1-100之内全部的质数或者说素数。
质数或素数定义
质数大于等于2 不能被它自己和1之外的数整除
作法:在通常领域,对正整数n,若是用2到sqrt(n)
之间的全部整数去除,均没法整除,则n为质数。
代码:
function getPrimes(arr){ return arr.filter(function(x){ if(x <= 3) return x>1; // 大于等于2才能够为质数,因此1除外 // 2到sqrt(n)之间均没法整除,即只要有一个能够整除即非质数。 var end = Math.round(Math.sqrt(x)); for(let i=2; i <= end; i++){ if(x % i === 0) return false; } return true; }); }
[1, 2,3,10,24].sort();
输出结果是[1, 10, 2, 24, 3]。javascript或默认将数组里数字转换成string去比较,那么根据ASCII嘛,天然是1要比2小,因此10就在2的前面。
But, 高阶函数嘛,那么天然sort也是能够接受函数来进行修改比较算法的,相似于要实现Java里的comparable接口,规则也是相似的,若x < y, 则返回-1,表示小于;若x === y, 返回0;若x > y,则返回1。理论上,并不要定义为1或者-1,只要用正数表示大于,负数表示小于就行了。因此你能够直接使用x-y;
那就数字排个序好了。
arr = [12343,5435,6,7,2,3,77,88,322]; arr.sort(function(x, y){ return x-y; });
Caution:这个sort方法会将排序结果直接做用在数组自己上,即会修改arr。
高阶函数除了能够接受函数做为参数外,还能够把函数做为结果值返回。
好比返回求和的函数。
function lazy_sum(arr){ var sum = function(){ return arr.reduce(function(x, y){ return x+y; }); return sum; } // 使用时 var f = lazy_sum([1,3,2,5]); f(); var f1 = lazy_sum([1,3,2,5]); f === f1; // result is false.
因为返回的是个函数,即便传入的参数相等,也至关因而返回了不一样的函数对象。
返回闭包时牢记的一点就是:返回函数不要引用任何循环变量,或者后续会发生变化的变量。
这个例子返回的函数引用了局部变量, 若是局部变量后续会变化,那么最好是再建立一个函数,绑定局部变量做为参数。
建立匿名函数并马上执行.
(function (x) { return x * x }) (3);
其中function (x) { return x * x }是匿名函数,须要使用括号括起来才能够马上执行,否则会报语法错误。
使用闭关封装一个私有变量private。
function create_counter(initial){ var x = initial || 0; return { inc:function(){ x += 1; return x; } } }
create_counter()返回了一个匿名对象,这个对象有个属性inc,而这个inc的值又是一个函数。那么就能够获取这个匿名对象,而后调用函数inc()便可实现对private的x加1.
缩减参数列表的用法
function make_pow(n){ return function(x){ return Math.pow(x,n); } } var pow2 = make_pow(2);// 返回一个Math.pow(x, 2)的函数。 var pow3 = make_pow(3);// 返回一个Math.pow(x, 3)的函数。
pow2 和 pow3就只须要接收一个参数,便可实现power的操做。
底下的Lambda表达式加法没能理解呀。
ES6 新引入的
x => x * x
至关于以下的匿名函数,其中x为参数。
function (x){ return x*x; }
若是有多个参数须要使用括号括起来,如(x,y) => x+y;
。
用法呢和匿名函数也差很少,好比:
var pow2 = x=> x*x; pow2(2); // 4 // 建立匿名函数并执行 (x => x*x)(2); // 4
还有一种包含多条语句的,须要使用{}
将其包起来。如:
(x, y) => { if(x>y){ return x-y;} else{ return y-x;} }
还有相似于()=>3.14
的写法,虽然看起来没啥卵用。
可变参数写法,就是参数变了一下
(x, y, ...rest) =>{ var sum= x =y; for(var x of arguments){ sum += x; } return sum; }
廖老师说x => { foo: x }
这样写会语义错误,然而并无。可是确实没可以被正确解析,因此当返回匿名对象的时候,使用()
将其包起来,如x => ({ foo: x })。
须要注意的是箭头函数内部的this是词法做用域,由上下文肯定。箭头函数彻底修复了this的指向,this老是指向词法做用域,也就是外层调用者obj。那么在第4节里说的每一个函数都自带apply和call方法,他们的第一个参数object就都不在有用,由于箭头函数会自动绑定外层调用者obj。
ES6新引入的数据类型,看上去像是一个函数,可是能够返回屡次。
与函数定义不一样的是,使用function*
来定义一个generator,除了使用return退出函数以后,还可使用yield交出控制权,这时并无退出整个函数,使用next时,就会从当前yield处往下执行。
以斐波那契数列为例:[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377]
普通的使用数组来保存结果的斐波那契数列生成方法
function fib(max){ var t, a = 0, b = 1, arr = [0, 1]; while (arr.length < max){ t = a + b; a = b; b = t; arr.push(t); } return arr; }
以generator生成器生成斐波那契数列的方法。
function* fib2(max){ var t, a = 0, b = 1, n = 1; while (n < max){ yield a; t = a + b; a = b; b = t; n++; } return a; } > var f = fib2(5); undefined > f.next(); // 输出0返回。 Object {value: 0, done: false} > f.next(); Object {value: 1, done: false} > f.next(); Object {value: 1, done: false} > f.next(); Object {value: 2, done: false} > f.next(); Object {value: 3, done: true} > f.next(); Object {value: undefined, done: true}
能够看到每次调用next,都会促使整段代码执行到下一个yield,而后输出一个匿名对象{value:xx, done:true/false}
其中xx是yield的返回值,done是代表此时是否生成结束,便是否执行到return。
还有一种使用方式,就是for...of
,无需判断是否执行完毕。
for (var x of fib(5)) { console.log(x); // 依次输出0, 1, 1, 2, 3 }
But这个generator有啥卵用呢?我暂时想起来一个状态转换的,即每次调用都会切换到下一个状态,直到切换完成,像Android中的StateMachine同样。