javaScript学习笔记(2)

1、arguments关键字
1.1、只在函数内部起作用,指向当前函数调用者传入的所有参数;类似于数组,但又不是数组;
1.2、arguments一般用于判断传入的参数个数;
1.3、由于JavaScript函数允许接收任意个参数,于是我们就不得不用arguments来获取所有参数;
2、rest参数
2.1、rest参数是es6的新标准;
2.2、如果想获得除了已经定义的参数以外的其他所有参数,可以使用rest,用法如下:function newFunc(a,b,...rest){}.多余的参数以数组形式交给变量rest;
3、变量2
3.1、一个变量如果在函数内声明,则它只能在函数内有效,在函数外面无法被使用,相反如果变量申明不在任何一个函数体内,那么他就是全局的。
3.2、不同的函数申明变量时使用同一个变量名不会有问题,两个在各自的函数体内使用,互不影响;
3.3、JavaScript的函数可以嵌套,此时,内部函数可以访问外部函数定义的变量,反过来则不行;
3.4、如果嵌套函数里面有相同的变量名,在使用变量时从自己函数内部先找,找到了则屏蔽外面相同名称的变量。
3.5、变量提升:即函数中所有的变量申明都会提升到函数的顶端。如:
function newFuc(){
var str = "Hello " + a ;
console.log(str);
var a = "boys" ;
}
以上的代码并不会报错,但是输出的结果却是:Hello undefined,可见js引擎自动提升了变量a的声明,但不会提升变量a的赋值;所以我们最好在函数的开始就申明好变量。
3.6、全局作用域
3.6.1、不在任何函数内定义的变量就具有全局作用域。实际上,JavaScript默认有一个全局对象window,全局作用域的变量都被绑定到window的一个属性上;如全局变量a,和window.a是一样的;
3.6.2、以变量方式var foo = function () {}定义的函数实际上也是一个全局变量,因此,顶层函数的定义也被视为一个全局变量,并绑定到window对象。
3.7、名字空间
3.7.1、全局变量会绑定到window上,不同的JavaScript文件如果使用了相同的全局变量,或者定义了相同名字的顶层函数,都会造成命名冲突,并且很难被发现。减少冲突的一个方法是把自己的所有变量和函数全部绑定到一个全局变量中,如:
var myVariable = {};//唯一的全局变量,其实就是一个对象,想要定义为全局变量的变量,加到他的属性中,就可以避免冲突。
myVariable.x = 1;
myVariable.y = 2;
3.8、块级作用域,在代码块中(如for(var i = 1 ; i < 100 ; i++))申明的变量在代码块外(函数内)也可以使用,用let关键在替代var申明变量,则可以是这个变量只在定义的代码块内起作用。
3.9、常量申明:const关键字是es6的新标准,用它可以申明一个变量,同时他和let一样,拥有块级作用域。
3.10、解构赋值:es6中的新标准,他可以同时对多个变量进行赋值,如:var [x,y,z] = [1,2,3],解构赋值有很多的应用场景,请大家自行google。
4、方法
4.1、函数被绑定到对象上,就称这个函数是这个对象的方法;
4.2、this关键字指向方法的调用者;
4.3、要指定函数的this指向哪个对象,可以用函数本身的apply方法,它接收两个参数,第一个参数就是需要绑定的this变量,第二个参数是Array,表示函数本身的参数;还可以使用call关键字,其使用方法和apply类似,只不过参数不用打包成array进行传递,直接接在第一个参数后面,有几个传几个。
4.4、装饰器(可以动态改变函数的行为)
4.4.1、js的所有对象都是动态的,即使是系统内置的函数都可以重新指向新的函数。例如:
var count = 0;
var oldParseInt = parseInt; // 保存原函数

window.parseInt = function () {
    count += 1;
    return oldParseInt.apply(null, arguments); // 调用原函数
};
console.log(parseInt("33"));
console.log(parseInt("31"));
console.log(parseInt("32"));
console.log(parseInt("34"));
console.log(count);//输出4


5、高阶函数
5.1、js的函数都指向一个变量,既然这样函数就可以接受一个函数作为参数,这样的函数就称为高阶函数,例:


function AddAbs(x,y,f){
return f(x) + f(y);
}


var sum = AddAbs(-3,6,Math.abs);
cosole.log(sum); //9


5.2、高阶函数map: map函数定在在Array里面,Array可以调用,并且作用到每一个元素上。例:
var arr = [2,3,-2,-6,7,-9];
var newArr = arr.map(Math.abs);
console.log(newArr);//[2, 3, 2, 6, 7, 9]
//上面的例子虽然也可以用循环实现,但是毕竟这样简便,同时也可以一眼明了是吧Math.abs 分别作用于Array的没一个元素上面。


5.3、高阶函数reduce:这个函数比较绕人,他同样是Array的,他从左到右对数组进行合并(累加,累乘或者自定义的操作),最后合并为一个值;看一个简单的例子:
var arr = [1,2,3,4];
function add(x,y){
return x+y;
}
var sum = arr.reduce(add);
console.log(sum);//累加的结果为:10


函数第一次执行的时候传入1,2进行累加,结果为:3,再用这个结果3,和第三个元素3作为参数传入,以此类推到最后 合并为10;
详见:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce


5.4、高阶函数filter:用于数组的筛选。他和map类似,会一次作用在每一个元素上,但是和map不一样的是他不会返回回调函数的返回值,而是根据返回的true or false 来决定是否保留传入的元素。例如:
//保留数组中大于5的元素
var arr = [2,4,55,4,65,32,3,54];
var newArr = arr.filter(function(x){
return x > 5;
})
console.log(newArr);//[55, 65, 32, 54]


5.5、sort方法也是一个高阶函数,我们可以自定义排序的规范,例如:
//对array从大到小进行排序
var arr= [21,14,1,5,3];
var newArr = arr.sort();
console.log(newArr);//结果竟然是[1, 14, 21, 3, 5],因为比较的时候吧元素转为string进行比较了。


//自定义排序
newArr = arr.sort(function(x,y){
    if(x >= y){
    return 1 ;
    }else{
    return -1 ;
    }
});
console.log(newArr); //[1, 3, 5, 14, 21] 正常了 
console.log(arr);    //也变成了:[1, 3, 5, 14, 21] 要注意,sort 和上面的map和reduce不一样,他是会在原来的array上直接修改,不会产生一个新的array


6、闭包:
6.1、函数作为返回值:函数既然可以作为参数传递,同样也可以作为返回值:


//函数作为返回值
function returnFunc(arr){
//函数内部定义一个函数
var newFunc =  function(){
var sum = arr.reduce(function(x,y){
return x+y;
});
return sum ;
}
//返回这个函数
return newFunc ;
}

console.log(returnFunc([1,2,4,5]));//调用这个函数,返回的不是结果,而是一个函数
var f = returnFunc([1,2,4,5]);
console.log(f());//执行这个方法才会返回结果:12


6.2、上面的例子中,returnFunc函数内部定义了一个函数newFunc,最后将这个函数返回,newFunc中使用了外部函数returnFunc的参数和局部变量,返回的newFunc中保存着这些参数和变量,这种程序结构就称为闭包。请再注意一点,当我们调用returnFunc时,每次调用都会返回一个新的函数,即使传入相同的参数:
var f1 = returnFunc([1, 2, 3, 4, 5]);
var f2 = returnFunc([1, 2, 3, 4, 5]);
f1 === f2; // false
//并且f1()和f2()的调用结果互不影响。

6.3、在返回闭包的函数时候要特别注意,不要引用任何后续可能会变化的变量,容易造成错误:
//把三个函数打包成array,一起返回
function func1(){
var arr = [];
for(var i = 1 ; i<=3 ;i++){
arr.push(function(){
return i * i;
})
}
return arr ;
}
var ss = func1();
console.log(ss[0]());
console.log(ss[1]());
console.log(ss[2]());
//以上的输出的结果我们认为分别是1,4,9,但是实际上全是16,因为循环变量最后变成了4;


//用下面的写法可以得到我们想要的结果。
function func1(){
var arr = [];
for(var i = 1 ; i<=3 ;i++){
(function(n){
arr.push(function(){
return n * n;
});
})(i);//创建匿名函数,并立即调用。
}
return arr ;
}


6.4、闭包的功能十分强大,闭包也可以认为是携带状态的函数,并且他的状态可以完全对外隐藏起来。看两个例子:


//计数器
function create_counter(starNum){
var x = starNum || 0 ;//starNum有值就传给x,没有聚默认是0;
return{
inc:function(){
x += 1 ;
return x ;  //该闭包携带了局部变量x
}
}
}


var c1 = create_counter(5);
console.log(c1.inc());//6
console.log(c1.inc());//7
console.log(c1.inc());//8
console.log(c1.inc());//9


var c2 = create_counter(0);
console.log(c2.inc());//1
console.log(c2.inc());//2
console.log(c2.inc());//3
console.log(c2.inc());//4


//闭包还可以把多参数的函数变成单参数的函数。例如,要计算x的y次方可以用Math.pow(x, y)函数,不过考虑到经常计算x2或x3,我们可以利用闭包创建新的函数pow2和pow3;
function createPow(n){
var newPow = function(x){
return Math.pow(x, n)
}
return newPow;
}
var pow2 = createPow(2);
console.log(pow2(3));    //9
console.log(pow2(4));    //16

var pow3 = createPow(3);
console.log(pow3(3));    //27
console.log(pow3(4));    //64


7、箭头函数
7.1、箭头函数很简单,看一个例子:
x => x + 1; // 就这么简单明了,他相当于下面这个函数


function (x){
return x + 1 ;
}


7.2、箭头汗水与就相当于是一个匿名函数,箭头函数有两种格式,一种像上面的,只包含一个表达式,连{ ... }和return都省略掉了。还有一种可以包含多条语句,这时候就不能省略{ ... }和return;如:
var ss = x => {
if(x > 0){
return x ;
}else{
return -x ;
}


console.log(ss(-23));    //23


7.3、如果要返回一个对象,就要注意,如果是单表达式:
// SyntaxError:
x => { foo: x }


//要加上一个括号,如:
x => ({ foo: x })


7.4、箭头函数还是和匿名函数有很大区别的:箭头函数内部的this是词法作用域,由上下文确定。


//下面的这个例子中,由于js中函数的this指向的bug,我们不能得到想要的结果
var obj = {
    birth: 1990,
    getAge: function () {
        var b = this.birth; // 1990
        var fn = function () {
            return new Date().getFullYear() - this.birth; // this指向window或undefined
        };
        return fn();
    }
};


//以下用箭头函数的方式进行修改后,可以实现;


var obj = {
    birth: 1990,
    getAge: function () {
        var b = this.birth; // 1990
        var fn =  () => {
            return new Date().getFullYear() - this.birth;  //this指向obj
        };
        return fn();
    }
};


8、generator(生成器)
8.1、生成器很像是一个函数,就连他们的定义也很相似,生成器用 function* 来定义,看上去也比较相似,但是他可以返回多次,函数只能返回一次。例:
function* func1(x){
yield x + 1 ;
yield x + 2 ;
yield x + 3 ;
return x + 4 ;
}
var f = func1(5);
//调用的时候不一样,要用.next()
console.log(f.next());//返回一个对象:{value: 6, done: false} -- done: false 表示还没全部执行完
console.log(f.next());//返回一个对象:{value: 7, done: false}
console.log(f.next());//返回一个对象:{value: 8, done: false}
console.log(f.next());//返回一个对象:{value: 9, done: false} --全部返回完了   继续执行没有必要
console.log(f.next());//返回一个对象:{value: undefined, done: true}  --返回undefined


//循环迭代输出
for(var mac of func1(5)){
console.log(mac); //依次输出6,7,8,想一下为啥没有9?
}


8.2、生成器可以看成是能记住代码执行状态的函数,生成器执行完依次之后会记住当前的状态,下次调用的时候还在这个状态下进行,看一个next_id的例子:
function* next_id(first_id){
var x = first_id || 0;
while(true){
yield x++ ;
}
}


var f = next_id(1);
console.log(f.next().value); //1
console.log(f.next().value); //2
console.log(f.next().value); //3
console.log(f.next().value); //4


8.3、generator还有另一个巨大的好处,就是把异步回调代码变成“同步”代码,晚一点再学习这个特性。

猜你喜欢

转载自blog.csdn.net/maaici/article/details/79547121
今日推荐