1. 基本概念

VAR CONST 和LET

在ES6出现之前,用var来声明变量,如果不小心忘记了var而直接定义变量的话,变量会自动成为全局变量从而可能污染整个项目。const 和let是在ES6之后出现的,const用来定义常量而let用来定义局部变量。有趣的是const定义的常量可能并不是一成不变的,如果定义的是复杂类型,可能只是表示const对象的不变,而不代表对象内容不可变(如果有c语言基础,将复杂对象的定义看作指针应该更恰当一些)。比如说:

  1. const n=5; //定义常量n=5
  2. n=6 //编译器报错,因为const对象的不可变性
  3. const result={}; //定义常量result
  4. result.first='this'; //result并没有变,变化的是其内容
  5. result.last='that';
  6. console.log('result'); //不会报错,输出result的内容
  7. //Object { first: "this", last: "that" }

一元运算符

符号+和-除了表示加法和减法外,在JavaScript中还有座位一元运算符的作用,用法为+x,-x,如果x不是数字,则通过一元运算符会自动转换为数字。但要注意的是,在JavaScript中++和—也是自增和自减法运算符,在连用时要和一元运算符区分。比如说:

  1. const x=5;
  2. const y=3- -x; //注意减号之间的空格,y=8
  3. const s='5';
  4. const t=3++s;//报错,因为类型不匹配
  5. const y=3+s;//‘35’转换成字符串加法
  6. const z=3+ +s;//z=8

严格相等与非严格相等

在JavaScript中,推荐使用===和!==的严格相等和严格不相等来进行判断。而非严格相等和非严格不相等用的是==和!=。主要区别在于,严格相等要求对象内容相同,并且类型相同,或者是同一个对象,或者可以转换成相同的值,而非严格相等则可能引起不必要的问题。因此,除非必要,尽量使用严格相等来判断。举例如下:

  1. const n= 5;
  2. const s='5';
  3. n===s;//false
  4. n==s;//true
  5. n===Number(s);//true
  6. const a={name:'something'};
  7. const b={name:'something'};
  8. a===b;//false,不是同一个对象
  9. a==b;//也是false,但不推荐使用

求值与表达式

  • 自增自减和赋值的副作用

    1. const skipIt=true;
    2. let x=0;
    3. const result=skipIt||x++;//result=true,x=0,跳过了x++的步骤
  • 条件运算符

    1. const doIt=false;
    2. const result=doIt?"did it":"Didn't do it";
  • 结构赋值

    1. const obj={a:2,b:3,c:4};
    2. const {a,b,c}=obj;//同时给a,b,c赋值
    3. let a=5,b=10;
    4. [a,b]=[b,a]//交换a和b的值
  • 模板字符串(注意不是单引号`)

    1. let temp=21.5;
    2. const tempString=`Current Temperature is ${temp}`
    3. //Current Temperature is 21.5

    2.函数与数组

    引用调用

    JavaScript里可以允许像调用值一样通过引用调用函数。

    1. function getGreeting(guestName){
    2. return 'Hello '+guestName;
    3. }
    4. const f=getGreeting;
    5. f('Joanna'); //"Hello Joanna"
    6. const o={};
    7. o.f=getGreeting;
    8. o.f('Henry') //"Hello Henry"
    9. console.log(o);
    10. const arr=[1,2,3];
    11. arr[1]=getGreeting;
    12. arr[1]('John');//"Hello John"
    13. arr //Array(3) [ 1, getGreeting(), 3 ]

    函数传值和传引用

    变量为基本类型的函数,是传值引用的,函数不会修改外部变量的值。但变量为对象类型参数的函数,是传引用调用的,函数对参数的修改会影响变量。从函数式编程的原则来说,应该尽量避免函数修改参数造成的函数不纯粹现象。

    展开操作符…

    展开操作符…可以用来接受任意多的参数,例如(本函数可以用foreach和箭头函数简单实现,此处仅供说明)

    1. function addPrefix(prefix,...words){
    2. const prefixedWords=[];
    3. for(let i=0;i<=words.length;i++){
    4. prefixedWords[i]=prefix+words[i];
    5. }
    6. return prefixedWords;
    7. }
    8. addPrefix('Mr.','John','Henry','Alex','Ken');
    9. //Array(5) [ "Mr.John", "Mr.Henry", "Mr.Alex", "Mr.Ken", "Mr.undefined" ]

    THIS的使用陷阱

    在函数中的this往往可能带来隐含的问题,在ES6中,通过箭头函数可以避免大部分这样的问题。在箭头函数出现之前,针对这一问题常见的解决办法是定义一个self(约定俗成)的变量来指向this。
    this带来的问题示例如下,在下面这个例子中,第一次调用speak时,有明确绑定,因此功能正常,但在第二次调用时由于指代不明会出现问题。因此,虽然可以使用self来解决一部分问题,但最好还是设法避开this陷阱。比如利用ES6的箭头函数功能。

    1. const o={
    2. name:'Henry',
    3. speak(){return `my name is ${this.name}`;},
    4. }
    5. o.speak(); //"my name is Henry"
    6. const speak=o.speak; //定义一个speak对象
    7. speak===o.speak; //true 同一个对象
    8. speak(); //"my name is " this没有被绑定的对象

    函数表达式、匿名函数和箭头函数

    1. const f=function(){
    2. return 'hello!';
    3. }; //匿名函数
    4. const g=function f(){
    5. return 'hello!';
    6. } //函数表达式,一般用来将g传入到其他函数中
    7. const f1=()=>'hello!'; //箭头函数

    调用,请求和绑定

    可以用来指定this的三种方法。调用 call, 请求 apply ,绑定 bind。区别在于call像一般函数那样获取参数,apply以数组的方式获取参数,在ES6中展开运算符…可以实现apply的功能。

    作用域、闭包和即时调用函数

    为了减少对全局变量的污染,程序中应该尽量仅在必要的时候使用全局变量。并借鉴函数式编程的思维,使函数的调用不依赖于外部参数。保证函数的纯净性。
    闭包指将函数定义在一个指定的作用域中,明确指出其对作用域具有访问权限。例如:

    1. let globalFunc;
    2. {
    3. let blockVar='a';
    4. globalFunc=function(){
    5. console.log('this is '+blockVar);
    6. }
    7. }
    8. globalFunc(); //this is a

    又比如,指定一个能记录自己被调用次数的函数。

    1. const g=(()=>{
    2. let count=0;
    3. return ()=>{
    4. return `I have been called ${++count} times`;
    5. }
    6. })();
    7. g();//"I have been called 1 times"
    8. g();//"I have been called 2 times"

    数组的内容操作说明

    基础操作:

  • push ,pop, 入栈、出栈,在结尾位置LIFO,修改数组

  • unshift, shift, 入队列,出队列,在队首FIFO,修改数组
  • concat, 在数组末尾添加元素,返回拷贝
  • slice(起始位置,终止位置), 获取子数组,返回拷贝
  • splice(起始位置,要删除的个数不删除为0,…要添加的元素),从任意位置添加或删除元素,修改数组
  • copyWithin(复制的目标位置,复制的起始位置,[可选]复制到哪里结束),剪切并替换元素,修改数组
  • fill(填充值,起始位置,终止位置),填充数组,修改数组
  • reverse,反转数组,修改数组
  • sort((alb)=>a.name>b.name),排序,可以设置排序条件修改数组

高级操作:

  • map,转化数组中所有元素,返回拷贝
  • filter,根据条件排除元素,返回拷贝
  • reduce,整个转换,返回拷贝
  • join,转化成字符串并合并,返回拷贝

    迭代与生成器

    通过数组的.values方法可以获得其可迭代对象,通过通配符*定义可以使函数成为生成器,在生成器中使用yield可以将控制权返回调用方。使用next可以调用迭代器的下一个值。迭代结束中其 ‘done’参数返回值为true。通过[Symbol.iterator]可以为生成迭代对象。例如。
    1. class Logs{
    2. constructor(){
    3. this.messages=[];
    4. }
    5. addMessage(message){
    6. this.messages.push({message,timestamp:Date.now()});
    7. }
    8. [Symbol.iterator](){
    9. return this.messages.values();
    10. }
    11. }
    12. const log=new Logs();
    13. log.addMessage('first day at sea');
    14. log.addMessage('spotted fishes');
    15. log.addMessage('new adventure to see');
    16. for (let msgs of log){
    17. console.log(`${msgs.message} @ ${msgs.timestamp}`);
    18. }