函数
参数
由于JavaScript允许传入任意个参数而不影响调用,因此传入的参数比定义的参数多也没有问题,虽然函数内部并不需要这些参数:
1 2 abs (10 , 'blablabla' ); abs (-9 , 'haha' , 'hehe' , null );
传入的参数比定义的少也没有问题:
此时abs(x)
函数的参数x
将收到undefined
,计算结果为NaN
。
要避免收到undefined
,可以对参数进行检查:
1 2 3 4 5 6 7 8 9 10 function abs (x ) { if (typeof x !== 'number' ) { throw 'Not a number' ; } if (x >= 0 ) { return x; } else { return -x; } }
arguments
它只在函数内部起作用,并且永远指向当前函数的调用者传入的所有参数。arguments
类似Array
但它不是一个Array
。
理解为一个参数列表,多用于判断参数个数。
rest
ES6引入的语法!
rest参数只能写在最后,前面用...
标识
1 2 3 4 5 function foo (a, b, ...rest ) { console .log ('a = ' + a); console .log ('b = ' + b); console .log (rest); }
用于接收剩下的参数,来替代arguments的复杂判断。
调用
apple
它接收两个参数,第一个参数就是需要绑定的this
变量,第二个参数是Array
,表示函数本身的参数。
1 2 3 4 5 6 7 8 9 10 11 12 13 function getAge ( ) { var y = new Date ().getFullYear (); return y - this .birth ; }var xiaoming = { name : '小明' , birth : 1990 , age : getAge }; xiaoming.age (); getAge.apply (xiaoming, []);
利用apple实现装饰器
1 2 3 4 5 6 7 var count = 0 ;var oldParseInt = parseInt ; window .parseInt = function ( ) { count += 1 ; return oldParseInt.apply (null , arguments ); };
call
同apple,唯一区别是:
apply()
把参数打包成Array
再传入;
call()
把参数按顺序传入。
比如调用Math.max(3, 5, 4)
,分别用apply()
和call()
实现如下:
1 2 Math .max .apply (null , [3 , 5 , 4 ]); Math .max .call (null , 3 , 5 , 4 );
作用域
全局作用域
JavaScript默认有一个全局对象window
,全局作用域的变量实际上被绑定到window
的一个属性
1 2 3 var course = 'Learn JavaScript' ;alert (course); alert (window .course );
我们每次直接调用的alert()
函数其实也是window
的一个变量
局部作用域
函数内为局部作用域
for循环等块语句是全局作用域!要声明局部变量使用let
关键字
命名空间
全局变量会绑定到window
上,不同的JavaScript文件如果使用了相同的全局变量,或者定义了相同名字的顶层函数,都会造成命名冲突,并且很难被发现。
减少冲突的一个方法是把自己的所有变量和函数全部绑定到一个全局变量中
1 2 3 4 5 6 7 8 9 10 11 var MYAPP = {};MYAPP .name = 'myapp' ;MYAPP .version = 1.0 ;MYAPP .foo = function ( ) { return 'foo' ; };
把自己的代码全部放入唯一的名字空间MYAPP
中,会大大减少全局变量冲突的可能。
解构赋值
(拆包)
1 2 3 4 let [x, [y, z]] = ['hello' , ['JavaScript' , 'ES6' ]]; x; y; z;
快速获取对象的指定属性
1 2 3 4 5 6 7 8 var person = { name : '小明' , age : 20 , gender : 'male' , passport : 'G-12345678' , school : 'No.4 middle school' };var {name, age, passport} = person;
可以直接对嵌套的对象属性进行赋值,只要保证对应的层次是一致的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 var person = { name : '小明' , age : 20 , gender : 'male' , passport : 'G-12345678' , school : 'No.4 middle school' , address : { city : 'Beijing' , street : 'No.1 Road' , zipcode : '100001' } };var {name, address : {city, zip}} = person; name; city; zip; address; let {name, passport :id} = person; name; id; passport; var {name, single=true } = person; name; single;
this指向问题
对象里的函数叫方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 function getAge ( ) { var y = new Date ().getFullYear (); return y - this .birth ; }var xiaoming = { name : '小明' , birth : 1990 , age : getAge }; xiaoming.age (); getAge (); var fn = xiaoming.age ; fn ();
1 2 3 4 5 6 7 8 9 10 11 12 13 var xiaoming = { name : '小明' , birth : 1990 , age : function ( ) { function getAgeFromBirth ( ) { var y = new Date ().getFullYear (); return y - this .birth ; } return getAgeFromBirth (); } }; xiaoming.age ();
this
指针只在age
方法的函数内指向xiaoming
,在函数内部定义的函数,this
又指向undefined
了!(在非strict模式下,它重新指向全局对象window
!)
修复的办法也不是没有,我们用一个that
变量首先捕获this
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 var xiaoming = { name : '小明' , birth : 1990 , age : function ( ) { var that = this ; function getAgeFromBirth ( ) { var y = new Date ().getFullYear (); return y - that.birth ; } return getAgeFromBirth (); } }; xiaoming.age ();
箭头函数修复了this指向的问题!
1 2 3 4 5 6 7 8 9 var obj = { birth : 1990 , getAge : function ( ) { var b = this .birth ; var fn = ( ) => new Date ().getFullYear () - this .birth ; return fn (); } }; obj.getAge ();
this
总是指向词法作用域,也就是外层调用者obj
this
在箭头函数中已经按照词法作用域绑定了,所以,用call()
或者apply()
调用箭头函数时,无法对this
进行绑定,即传入的第一个参数被忽略:
1 2 3 4 5 6 7 8 9 var obj = { birth : 1990 , getAge : function (year ) { var b = this .birth ; var fn = (y ) => y - this .birth ; return fn.call ({birth :2000 }, year); } }; obj.getAge (2015 );
生成器
1 2 3 4 5 function * foo (x ) { yield x + 1 ; yield x + 2 ; return x + 3 ; }
generator由function*
定义(注意多出的*
号),并且,除了return
语句,还可以用yield
返回多次。
调用generator对象有两个方法,一是不断地调用generator对象的next()
方法,next()
方法会执行generator的代码,然后,每次遇到yield x;
就返回一个对象{value: x, done: true/false}
,然后“暂停”。返回的value
就是yield
的返回值,done
表示这个generator是否已经执行结束了。如果done
为true
,则value
就是return
的返回值。
第二个方法是直接用for ... of
循环迭代generator对象,这种方式不需要我们自己判断done
。
对象
继承
JavaScript由于采用原型继承,我们无法直接扩展一个Class
JavaScript的原型继承实现方式就是:
定义新的构造函数,并在内部用call()
调用希望“继承”的构造函数,并绑定this
;
借助中间函数F
实现原型链继承,最好通过封装的inherits
函数完成;
继续在新的构造函数的原型上定义新方法。
1 2 3 4 5 6 7 function inherits (Child, Parent ) { var F = function ( ) {}; F.prototype = Parent .prototype ; Child .prototype = new F (); Child .prototype .constructor = Child ; }
class继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Student { constructor (name ) { this .name = name; } hello ( ) { alert ('Hello, ' + this .name + '!' ); } }class PrimaryStudent extends Student { constructor (name, grade ) { super (name); this .grade = grade; } myGrade ( ) { alert ('I am at grade ' + this .grade ); } }
高阶函数
map
map(fn)
1 2 3 4 5 let ns = [1 ,2 ,3 ];let new_ns = ns.map (function (n ){ return n * 2 })
reduce
reduce(fn, preDefault)
1 2 3 4 5 let ns = [1 ,2 ,3 ];let new_ns = ns.reduce (function (preValue,n ){ return preValue + n }, 0 )
filter
filter(fn)
1 2 3 4 5 let ns = [1 ,2 ,3 ];let new_ns = ns.filter (function (preValue,n ){ return n >= 2 })
模块导入导出
ES6
因为直接引用script是导入到全局作用域,会导致变量名重复等问题。
解决:以模块为变量作用域,通过导入导出的方式来提供复用。
1 2 3 4 <script src ="aaa.js" type ="module" > </script > <script src ="bbb.js" type ="module" > </script > <script src ="ccc.js" type ="module" > </script >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 var flag = true ;var num = 100 ;export { flag, num }export var len = 10 ;export function add (n1, n2 ){ return n1 + n2 }var address = "北京市" ;export default address;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import {flag, num} from "./aaa.js" ;console .log (flag);console .log (num);import {len} from "./aaa.js" ;import {add} from "./aaa.js" ;console .log (add (len, num));import addr from "./aaa.js" ;console .log (addr);
1 2 3 4 5 6 import * as hah from "./aaa.js" ;console .log (hah.num );