1. 基本概念
VAR CONST 和LET
在ES6出现之前,用var来声明变量,如果不小心忘记了var而直接定义变量的话,变量会自动成为全局变量从而可能污染整个项目。const 和let是在ES6之后出现的,const用来定义常量而let用来定义局部变量。有趣的是const定义的常量可能并不是一成不变的,如果定义的是复杂类型,可能只是表示const对象的不变,而不代表对象内容不可变(如果有c语言基础,将复杂对象的定义看作指针应该更恰当一些)。比如说:
const n=5; //定义常量n=5
n=6; //编译器报错,因为const对象的不可变性
const result={}; //定义常量result
result.first='this'; //result并没有变,变化的是其内容
result.last='that';
console.log('result'); //不会报错,输出result的内容
//Object { first: "this", last: "that" }
一元运算符
符号+和-除了表示加法和减法外,在JavaScript中还有座位一元运算符的作用,用法为+x,-x,如果x不是数字,则通过一元运算符会自动转换为数字。但要注意的是,在JavaScript中++和—也是自增和自减法运算符,在连用时要和一元运算符区分。比如说:
const x=5;
const y=3- -x; //注意减号之间的空格,y=8
const s='5';
const t=3++s;//报错,因为类型不匹配
const y=3+s;//‘35’转换成字符串加法
const z=3+ +s;//z=8
严格相等与非严格相等
在JavaScript中,推荐使用===和!==的严格相等和严格不相等来进行判断。而非严格相等和非严格不相等用的是==和!=。主要区别在于,严格相等要求对象内容相同,并且类型相同,或者是同一个对象,或者可以转换成相同的值,而非严格相等则可能引起不必要的问题。因此,除非必要,尽量使用严格相等来判断。举例如下:
const n= 5;
const s='5';
n===s;//false
n==s;//true
n===Number(s);//true
const a={name:'something'};
const b={name:'something'};
a===b;//false,不是同一个对象
a==b;//也是false,但不推荐使用
求值与表达式
自增自减和赋值的副作用
const skipIt=true;
let x=0;
const result=skipIt||x++;//result=true,x=0,跳过了x++的步骤
条件运算符
const doIt=false;
const result=doIt?"did it":"Didn't do it";
结构赋值
const obj={a:2,b:3,c:4};
const {a,b,c}=obj;//同时给a,b,c赋值
let a=5,b=10;
[a,b]=[b,a]//交换a和b的值
模板字符串(注意不是单引号`)
let temp=21.5;
const tempString=`Current Temperature is ${temp}`
//Current Temperature is 21.5
2.函数与数组
引用调用
JavaScript里可以允许像调用值一样通过引用调用函数。
function getGreeting(guestName){
return 'Hello '+guestName;
}
const f=getGreeting;
f('Joanna'); //"Hello Joanna"
const o={};
o.f=getGreeting;
o.f('Henry') //"Hello Henry"
console.log(o);
const arr=[1,2,3];
arr[1]=getGreeting;
arr[1]('John');//"Hello John"
arr //Array(3) [ 1, getGreeting(), 3 ]
函数传值和传引用
变量为基本类型的函数,是传值引用的,函数不会修改外部变量的值。但变量为对象类型参数的函数,是传引用调用的,函数对参数的修改会影响变量。从函数式编程的原则来说,应该尽量避免函数修改参数造成的函数不纯粹现象。
展开操作符…
展开操作符…可以用来接受任意多的参数,例如(本函数可以用foreach和箭头函数简单实现,此处仅供说明)
function addPrefix(prefix,...words){
const prefixedWords=[];
for(let i=0;i<=words.length;i++){
prefixedWords[i]=prefix+words[i];
}
return prefixedWords;
}
addPrefix('Mr.','John','Henry','Alex','Ken');
//Array(5) [ "Mr.John", "Mr.Henry", "Mr.Alex", "Mr.Ken", "Mr.undefined" ]
THIS的使用陷阱
在函数中的this往往可能带来隐含的问题,在ES6中,通过箭头函数可以避免大部分这样的问题。在箭头函数出现之前,针对这一问题常见的解决办法是定义一个self(约定俗成)的变量来指向this。
this带来的问题示例如下,在下面这个例子中,第一次调用speak时,有明确绑定,因此功能正常,但在第二次调用时由于指代不明会出现问题。因此,虽然可以使用self来解决一部分问题,但最好还是设法避开this陷阱。比如利用ES6的箭头函数功能。const o={
name:'Henry',
speak(){return `my name is ${this.name}`;},
}
o.speak(); //"my name is Henry"
const speak=o.speak; //定义一个speak对象
speak===o.speak; //true 同一个对象
speak(); //"my name is " this没有被绑定的对象
函数表达式、匿名函数和箭头函数
const f=function(){
return 'hello!';
}; //匿名函数
const g=function f(){
return 'hello!';
} //函数表达式,一般用来将g传入到其他函数中
const f1=()=>'hello!'; //箭头函数
调用,请求和绑定
可以用来指定this的三种方法。调用 call, 请求 apply ,绑定 bind。区别在于call像一般函数那样获取参数,apply以数组的方式获取参数,在ES6中展开运算符…可以实现apply的功能。
作用域、闭包和即时调用函数
为了减少对全局变量的污染,程序中应该尽量仅在必要的时候使用全局变量。并借鉴函数式编程的思维,使函数的调用不依赖于外部参数。保证函数的纯净性。
闭包指将函数定义在一个指定的作用域中,明确指出其对作用域具有访问权限。例如:let globalFunc;
{
let blockVar='a';
globalFunc=function(){
console.log('this is '+blockVar);
}
}
globalFunc(); //this is a
又比如,指定一个能记录自己被调用次数的函数。
const g=(()=>{
let count=0;
return ()=>{
return `I have been called ${++count} times`;
}
})();
g();//"I have been called 1 times"
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]可以为生成迭代对象。例如。class Logs{
constructor(){
this.messages=[];
}
addMessage(message){
this.messages.push({message,timestamp:Date.now()});
}
[Symbol.iterator](){
return this.messages.values();
}
}
const log=new Logs();
log.addMessage('first day at sea');
log.addMessage('spotted fishes');
log.addMessage('new adventure to see');
for (let msgs of log){
console.log(`${msgs.message} @ ${msgs.timestamp}`);
}