廖雪峰的JavaScript教程进修笔记
1. 变量作用域
var
不能声明块级的变量,js的函数内变量声明会被提升至函数体开首let
则用来处置惩罚这个块级变量声明,于ES6引入。const
用于声明常量,也是ES6引入。
2. 定名空间
全局变量会被默许绑定到window
,差别JS文件假如定义了雷同的称号的全局变量或许顶级函数,那末就会致使争执。因此,处置惩罚要领就是把自身的全局变量绑定到一个全局变量中,相似于Java中的class,个人觉得。
3.this 关键字
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。
4. apply 和 call
每一个函数都自带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]);
5. 装潢器
javascript一切的对象都是动态,纵然是内置的函数,也可以从新指向新的函数。
如window上有个全局函数parseInt()函数,须要统计parseInt被挪用次数。
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哭晕在茅厕。
6. 高阶函数
定义:高阶函数英文叫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
7. 高阶函数map/reduce
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是不是会提醒函数或许要领的参数为啥。
8.高阶函数filter
语法: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;
});
}
9.高阶函数sort
[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。
10.闭包
高阶函数除了可以吸收函数作为参数外,还可以把函数作为效果值返回。
比方返回乞降的函数。
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表达式加法没能明白呀。
11.箭头函数
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。
12. generator
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一样。