变量声明const和let
在ES6之前,我们都是用var关键字声明变量。无论声明在何处,都会被视为声明在函数的最顶部(不在函数内即在全局作用域的最顶部)。这就是函数变量提升例如:
function fn(flag){
if(flag){
var test = "hello es6";
}else{
console.log( test ); // undefined
}
}
fn();
这里flag明显为空,也就是false,判断语句执行的是else代码块里的,但没有报错,而是undefined,因为,这是变量test已经声明,只是没有赋值,上面代码,实际上是:
function fn(flag){
var test; // 变量提升
if(flag){
test = "hello es6"; // 变量赋值
}else{
console.log( test ); // undefined
}
}
fn();
所以不用关心flag是否为 true or false。实际上,无论如何 test 都会被创建声明。
接下来ES6主角登场:
我们通常用 let 和 const 来声明,let 表示变量、const 表示常量。let 和 const 都是块级作用域。怎么理解这个块级作用域?
- 在一个函数内部
- 在一个代码块内部
说白了只要在花括号( {} )内的代码块即可以认为 let 和 const 的作用域。
例如:
function fn(flag){
if(flag){
let test = "hello es6";
}else{
console.log( test ); // 报错 test is not defined
}
}
fn();
let 的作用域是在它所在当前代码块,但不会被提升到当前函数的最顶部。
再来说说 const
const 声明的变量必须提供一个值,而且会被认为是常量,意思就是它的值被设置完成后就不能再修改了,例如:
const myAge = 24;
myAge = 25; // 报错 Assignment to constant variable
还有,如果 const 的是一个对象,对象所包含的值是可以被修改的。抽象一点儿说,就是对象所指向的地址不能改变,而变量成员是可以修改的。例如:
const student = {
name: "tom",
age: 24,
links: "read"
}
student.links = "sing"; // 不会报错,修改的是对象的属性
说说TDZ(暂时性死区),想必你早有耳闻。
console.log( links );
let links = "sing"; // 报错 links is not defined
我们都知道,JS引擎扫描代码时,如果发现变量声明,用 var 声明变量时会将声明提升到函数或全局作用域的顶部。但是 let 或者 const,会将声明关进一个小黑屋也是TDZ(暂时性死区),只有执行到变量声明这句语句时,变量才会从小黑屋被放出来,才能安全使用这个变量。
哦了,说一道面试题:
var fns = [];
for( var i = 0; i < 10; i++ ){
fns.push(
function(){ console.log( i ) }
);
}
fns.forEach(function (fns) {
fns();
})
这样的面试题是大家很常见,很多同学一看就知道输出十次10
但是如果我们想依次输出0到9呢?
有两种解决方法,直接看一下代码:
// 闭包解决
var fns = [];
for( var i = 0; i < 10; i++ ){
fns.push(
(function(j){
return function () {
console.log( j );
}
})(i)
);
}
fns.forEach(function (fns) {
fns();
})
再来看看es6的解决方法:
var fns = [];
for( let i = 0; i < 10; i++ ){
fns.push(
function(){
console.log( i );
}
);
}
fns.forEach(function (fns) {
fns();
})
达到相同的效果,ES6 简洁的解决方案是不是更让你心动!!!
字符串
ES6模板字符简直是开发者的福音啊,解决了 ES5 在字符串功能上的痛点。
第一个用途,基本的字符串格式化。将表达式嵌入字符串中进行拼接。用${}来界定。
// es5
var test1 = "tom";
console.log( "hello~ " + test1 ); // hello~ tom
// es6
let test2 = "alice";
console.log( `hello~ ${test2}` ); // hello~ alice
第二个用途,在ES5时我们通过反斜杠()来做多行字符串或者字符串一行行拼接。ES6反引号(``)直接搞定。
// es5
var test1 = "hello \
tom!\
";
console.log( test1 );
// es6
var test2 = `hello
alice~
`;
console.log( test2 );
还有一些比较常用的方法:
// 判断是否包含返回布尔值
let test1 = "hello~";
console.log( test.includes( "e" ) ); // true
// repeat 把字符串重复n次
let test2 = "la";
console.log( test2.repeat( 3 ) ); // lalala
// startsWith 和 endsWith 判断是否以 给定文本 开始或者结束
let test3 = "welcome es6~";
console.log(test3.startsWith( "we" ), test3.endsWith( "~" )); // true true
// 4. padStart 和 padEnd 填充字符串,应用场景:时分秒
setInterval(() => {
let now = new Date()
let hours = now.getHours().toString()
let minutes = now.getMinutes().toString()
let seconds = now.getSeconds().toString()
console.log(`${hours.padStart(2, 0)}:${minutes.padStart(2, 0)}:${seconds.padStart(2, 0)}`) // 14:14:02
}, 1000)
函数
函数默认参数
在ES5我们给函数定义参数默认值是怎么样?
function fn( num ){
num = num || 20;
console.log( num );
}
fn(); // 20
fn( 10 ); // 10
但细心观察的同学们肯定会发现,num传入为0的时候就是false,但是我们实际的需求就是要拿到num = 0,此时num = 200 明显与我们的实际想要的效果明显不一样。
ES6为参数提供了默认值。在定义函数时便初始化了这个参数,以便在参数没有被传递进去时使用。
function fn( num = 20 ){
console.log( num );
}
fn( 10 ); // 10
fn(); // 20
fn( 0 ); // 0
箭头函数
ES6很有意思的一部分就是函数的快捷写法。也就是箭头函数。
箭头函数最直观的三个特点。
- 不需要 function 关键字来创建函数
- 省略 return 关键字
- 继承当前上下文的 this 关键字
例如:
[1,2,3].map(x => x + 1)
// 等同于
[1,2,3].map((function(x){
return x + 1
}).bind(this))
当你的函数有且仅有一个参数的时候,是可以省略掉括号的。当你函数返回有且仅有一个表达式的时候可以省略{} 和 return;例如:
let fn = name => `hello ${name}`;
console.log( fn( "tom" ) ); // hello tom
如果有多个参数,和,不只有一个返回的表达式的时候就不能省略了,比如:
let fn = ( name, age ) => {
let test = "hello";
return `${name}今年${age}岁~`;
}
console.log( fn( "tom", 24 ) ); // tom今年24岁~
拓展的对象功能
对象初始化简写
ES5我们对于对象都是以键值对的形式书写,是有可能出现键值对重名的。例如:
function people( name, age ){
return {
name: name,
age: age
}
}
键值对重名,ES6可以简写如下:
function people( name, age ){
return {
name,
age
}
}
ES6 同样改进了为对象字面量方法赋值的语法。ES5为对象添加方法:
const people = {
name: "tom",
sayHi: function () {
console.log( `${this.name} 你好` );
}
}
people.sayHi(); // tom 你好
ES6通过省略冒号与 function 关键字,将这个语法变得更简洁
const people = {
name: "tom",
sayHi() {
console.log( `${this.name} 你好` );
}
}
people.sayHi(); // tom 你好
ES6 对象提供了 Object.assign()这个方法来实现浅复制。
Object.assign() 可以把任意多个源对象自身可枚举的属性拷贝给目标对象,然后返回目标对象。第一参数即为目标对象。在实际项目中,我们为了不改变源对象。一般会把目标对象传为{}
let objA = {
name: "tom"
}
let objB = {
age: 24
}
let objC = {};
let obj = Object.assign( objC, objA, objB );
console.log( objA ); // {name: "tom"}
console.log( objB ); // {age: 24}
console.log( objC ); // {name: "tom", age: 24}
console.log( obj ); // {name: "tom", age: 24}
更方便的数据访问 —— 解构
数组和对象是JS中最常用也是最重要表示形式。为了简化提取信息,ES6新增了解构,这是将一个数据结构分解为更小的部分的过程
ES5我们提取对象中的信息形式如下:
let people = {
name: "tom",
age: 24
}
let name = people.name;
let age = people.age;
console.log( name + " --- " + age ); // tom --- 24
是不是觉得很熟悉,没错,在ES6之前我们就是这样获取对象信息的,一个一个获取。现在,解构能让我们从对象或者数组里取出数据存为变量,例如:
// 对象
let people = {
name: "tom",
age: 24
}
let { name, age } = people;
console.log( `${name} --- ${age}` ); // tom --- 24
// 数组
let fruit = [ "苹果", "橘子" ];
let [ first, second ] = fruit;
console.log( first ); //苹果
console.log( second ); //橘子
Spread Operator 展开运算符
ES6中另外一个好玩的特性就是Spread Operator 也是三个点儿...接下来就展示一下它的用途。
组装对象或者数组
// 对象
let people1 = {
name: "tom",
age: 24
}
let people2 = {
...people1,
links: "sing"
}
console.log( people1 ); // {name: "tom", age: 24}
console.log( people2 ); // {name: "tom", age: 24, links: "sing"}
// 数组
let colors1 = [ "red", "green" ];
let colors2 = [ ...colors1, "blue" ];
console.log( colors1 ); // ["red", "green"]
console.log( colors2 ); // ["red", "green", "blue"]
有时候我们想获取数组或者对象除了前几项或者除了某几项的其他项,比如:
// 对象
let people1 = {
name: "tom",
age: 24,
links: "sing"
}
let { age, ...rest } = people1;
console.log( rest ); // {name: "tom", links: "sing"}
// 数组
let colors1 = [ "red", "green", "blue" ];
let [ first, ...rest ] = colors1;
console.log( rest ); // ["green", "blue"]
对于 Object 而言,还可以用于组合成新的 Object 。(ES2017 stage-2 proposal) 当然如果有重复的属性名,右边覆盖左边
// 数组
let frist = [ 1, 2, 3 ];
let second = [ 3, 4, 5 ];
let total = [ ...frist, ...second ];
console.log( total ); // [1, 2, 3, 3, 4, 5]
// 对象
let objA = {
name: "tom",
age: 24,
links: "sing"
}
let objB = {
address: "安庆",
links: "read"
}
let obj = { ...objA, ...objB };
console.log( obj ); // name: "tom", age: 24, links: "read", address: "安庆"}
import 和 export
import导入模块、export导出模块
//全部导入
import people from './example'
//有一种特殊情况,即允许你将整个模块当作单一对象进行导入
//该模块的所有导出都会作为对象的属性存在
import * as example from "./example.js"
console.log(example.name)
console.log(example.age)
console.log(example.getName())
//导入部分
import {name, age} from './example'
// 导出默认, 有且只有一个默认
export default App
// 部分导出
export class App extend Component {};
以前有人问我,导入的时候有没有大括号的区别是什么。下面是我在工作中的总结:
1.当用export default people导出时,就用 import people 导入(不带大括号)
2.一个文件里,有且只能有一个export default。但可以有多个export。
3.当用export name 时,就用import { name }导入(记得带上大括号)
4.当一个文件里,既有一个export default people, 又有多个export name 或者 export age时,导入就用 import people, { name, age }
5.当一个文件里出现n多个 export 导出很多模块,导入时除了一个一个导入,也可以用import * as example
网友评论