一、对象字面量扩展
1、属性的定义
ES6之前
1 2 3 4 5 6 |
var name = 'zs';
var age = 18;
var user = {"name":name,"age":age} ;
console.log(user);
|
ES6之后
1 2 3 4 5 6 7 8 |
var name = 'zs';
var age = 18;
// 生成的user对象中,属性名就是变量名,属性值为对应变量的值。
// 当然,必须先定义相关的变量
var user = {name,age} ;
console.log(user);
|
2、方法的定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
var user = {
// ES6之前
eat:function() {
console.log('吃');
},
// ES6后,省略冒号和functon关键字
sleep() {
console.log("睡觉");
}
}
user.eat() ;
user.sleep() ;
|
3、属性表达式
ES6之前
访问属性和方法有两种方式,分别为:
- 点操作符
- 对象.属性
- 对象.方法()
- []操作符
- 对象[属性名]
- 对象[方法名]()
- 注意,中括号中的名称支持使用表达式运算
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
var user = {
user_name: 'zs',
user_age: 18,
user_login: function () {
console.log('登录');
}
}
// 在中括号中,编写表达式,连接字符串,访问相关的属性和方法
// 点操作符
console.log(user.user_name);
// 中括号操作,支持表达式运算
console.log(user['user_' + 'age']);
user['user_' + 'login']();
|
ES6之后
对象的属性定义,支持使用表达式进行运算。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// 定义前缀字符串
var prefix = 'user_' ;
var user = {
// 定义属性时,支持使用表达式运算
[prefix+'name']: 'zs',
[prefix+'age']: 18,
[prefix+'login']: function () {
console.log('登录');
}
}
// 在中括号中,编写表达式,连接字符串,访问相关的属性和方法
console.log(user.user_name);
console.log(user['user_' + 'age']);
user['user_' + 'login']();
|
4、属性访问器
简化了Object.defineProperty()的存取数据描述的定义。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
var user = {
// 访问时,直接访问age即可,而_age是一种编程规范,并没有实现对外隐藏,外部依然可以直接访问
_age:0,
get id() {
return this._age ;
},
set id(value) {
this._age = value ;
}
}
user.age = 18 ;
console.log(user.age);
|
5、直接指定原型
1 2 3 4 5 6 7 8 9 10 11 |
var person = {
name:'匿名'
}
var user = {
// 直接通过 __proto__ 属性指定原型对象
__proto__:person
}
console.log(user);
console.log(user.name);
|
__proto__属性已在ES6中标准化。在文本标记法中,__proto__只是语法糖,可以方便的指定原型对象。本质上还是使用构造方法的prototype属性来指定原型对象的,即Object.prototype。
另外,以下方法,也可以指定原型对象
1 2 3 4 5 6 |
var user1 = {}
Object.setPrototypeOf(user1,person);
console.log(user1);
var user2 = Object.create(person) ;
console.log(user2);
|
6、super关键字
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 animal = {
name:'动物' ,
show() {
// 输出当前对象,查看this的指向
console.log(this);
console.log('我是一只 ' + this.name);
}
}
var cat = {
// 直接通过 __proto__ 属性指定原型对象
__proto__:animal ,
// 覆盖属性
name:'猫',
show() {
// 调用原型中的方法,有以下三种方式
// 方式一:在原型的show()方法中,this指向的还是cat对象
// super.show() ;
// 方式二:区别于super,在原型的show()方法中,this的还是cat对象
// Object.getPrototypeOf(this).show() ;
// 方法三:使用call方法调用,重新指定this的指向,使其指定cat对象,完全等同于super。
// 因此,super是一个语法糖,方便调用
Object.getPrototypeOf(this).show.call(this) ;
console.log('喵喵...');
}
}
cat.show() ;
|
二、块级作用域
1、Hoisting
变量和函数的提升(Hoisting),是JavaScript中执行上下文(特别是创建和执行阶段)一种特殊的工作方式。
1)变量提升
1 2 3 4 5 6 7 8 |
// undefined console.log(a); // b is not defined console.log(b); var a = 100 ; b = 200 ; |
在JavaScript中,var声明的变量具有Hoisting特性,JavaScript引擎在执行的时候,会把所有变量的声明都提升到当前作用域的最前面。上面代码编译后的结果为:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
// var声明的变量具有Hoisting特性,会在程序最前面进行声明,并初始值为undefined var a = undefined; // undefined console.log(a); // b is not defined console.log(b); a = 100 ; // 未使用var声明的变量,不具有Hoisting特性 b = 200 ; |
2)函数提升
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
fun1() ;
console.log(fun2);
fun2() ;
// 声明函数
function fun1() {
console.log('fun1');
}
// var定义的函数表达式 -- 文本标记定义,与变量var声明一样
var fun2 = function() {
console.log('fun2');
}
|
声明的函数具有hoisting特性,而var定义的函数表达式与变量一样会提升且赋值为undefined。上面代码编译后的结果为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// 声明式函数会提升在最前面
function fun1() {
console.log('fun1');
}
// 与变量声明一样,var声明的函数会提升在前面,且初始值为undefined
var fun2 = undefined ;
// 成功调用
fun1() ;
// 输出undefined
console.log(fun2);
// 因为fun2现在为undefined,因此报错
fun2() ;
|
注意:函数和变量的提升中,函数优秀级更高一点,即函数要比变量的提升到更高的位置。
2、let
与var一样,是用于声明变量的关键字(ES6新增)
1)作用域
let声明的变量支持块级作用域,只作用于当前块
1 2 3 4 5 6 7 |
if(true) {
var m = 100 ;
let n = 200 ;
}
console.log(m) ;
console.log(n) ;
|
2)变量提升
let声明的变量不会被”提升”
1 2 3 4 |
// undefined console.log(m) ; let m = 100 ; |
3)重复定义
let不允许重复定义相同的变量
1 2 3 4 |
let m = 100 ; let m = 100 ; // var可以,后者覆盖前者 |
4)暂时性死区
利用let声明的变量会绑定在这个块级作用域,不会受外界的影响。
1 2 3 4 5 6 7 8 9 10 11 |
var tmp = 123;
if (true) {
tmp = 'abc';
// 思考:输出结果为?why?
// console.log(tmp);
let tmp;
}
// 思考2:输出结果为?why?
// console.log(tmp) ;
|
暂时性死区指的是在当前作用域中,在使用 let 声明变量之前访问该变量会抛出 ReferenceError 错误,即使变量使用var声明也不例外。
也就是说,使用let或const声明的变量,只有在声明语句之后才能被访问。
思考题
1 2 3 4 5 6 7 8 9 10 |
var arr = [];
for (var i = 0; i < 2; i++) {
arr[i] = function () {
console.log(i);
}
}
// 以下两行代码,输出结果为?
arr[0]();
arr[1]();
|

说明:变量i是全局的,在调用函数之前,循环已经执行完毕,此时的循环变量的值为2。因此,在调用函数时,函数使用的变量i是最新的值。
1 2 3 4 5 6 7 8 9 10 11 12 |
let arr = [];
// let声明的变量i,在循环中都会产生一个块级作用域
for (let i = 0; i < 2; i++) {
arr[i] = function () {
console.log(i);
}
}
// 以下两行代码,输出的结果为?
arr[0]();
arr[1]();
|

说明:let关键字声明的变量具有块级作用域。每次循环都会产生一个块级作用域,每个块级作用域中的变量都是不同的。因此,调用函数时输出对应作用域下的i值。
课堂练习
在页面中,定义以下的无序列表:
- 你好
- 我好
- 大家好,才是真的好
编写JS,实现当用户单击以上无序列表时,弹出对应的文本内容及其下标索引。
提示:var实现 、let实现、闭包实现
3、const
声明常量,常量就是值(内存地址)不能变化的量。
1 2 |
// 在声明常量的同时,必须初始化值,且值在后面的使用中不能改变 const 常量名称 = 值 ; |
const与let基本相同,产生块级作用域,不会提升,存在暂时性死区,必须先声明后使用,不允许重复定义。
const限制的只是常量指向的内存地地不变,但地址所引用的对象是允许被修改的。因为简单类型的数据直接存放在内存地址中,而复杂类型(如对象各数组)的数据是间接存储的。如:
1 2 3 4 5 6 7 8 9 10 |
// 定义一个常量数组 const LIST = [100, 200]; LIST.push(300); // 常量对应的数组是允许进行相关的操作 console.log(LIST); // 错误,常量不允许重新赋值 LIST = [100,200,300]; |
小结
const用于声明常量
在声明常量的同时,必须初始值
常量不能重新进行赋值
- 如果是基本数据类型,不能更改值;
- 如果是复杂数据类型,不能更改地址值,但复杂类型的数据是允许修改的。
4、var、let、const区别
- 使用 var 声明的变量,其作用域为该语句所在的函数内,且存在变量提升现象
- 使用 let 声明的变量,其作用域为该语句所在的代码块内,不存在变量提升
- 使用 const 声明的是常量,在后面出现的代码中不能再修改该常量的值
三、函数默认值
1、默认参数值
ES6之前
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
function fun(m, n) {
// 参数默认值处理
m = m || 100 ;
n = n || 200 ;
console.log('m=' + m + ',n=' + n);
}
fun(1) ;
fun(1,2) ;
fun(1,undefined) ;
fun(undefined,2) ;
// 结果为?
fun(0,0) ;
|
ES6之后
在声明函数时,允许直接把默认值定义在形参中,如:
1 2 3 4 5 6 7 8 9 10 11 12 |
// 设置参数的默认值
function fun(m=100, n=200) {
console.log('m=' + m + ',n=' + n);
}
fun(1) ;
fun(1,2) ;
fun(1,undefined) ;
fun(undefined,2) ;
// 结果为?
fun(0,0) ;
|
如果没有传递实参,则使用默认值,否则使用传递的实参数据。
2、默认值表达式
1)函数默认值支持表达式运算
1 2 3 4 5 6 7 |
// 设置参数的默认值
function fun(m=100*2, n=200+m) {
console.log('m=' + m + ',n=' + n);
}
// 结果为?
fun() ;
|
2)函数默认值为自调用函数
1 2 3 4 5 6 7 8 |
function fun(m=100*2, n=(function(v){
return v + 200
})(200)) {
console.log('m=' + m + ',n=' + n);
}
fun() ;
|
四、spread与rest
1、spread:展开
语法:…名称
注意:
- 名称必须是一个可iterable的对象。
- 其中可iterable的对象有:数组、字符串、生成器(Generators)、集合(Collections)
- 作用:把可iterable的对象展开,并返回展开的内容
1)数组操作
1 2 3 4 5 6 |
var arr1 = [1, 2, 3]; // 展开arr1数组,并把展开的内容添加到arr2中 var arr2 = ['AA', ...arr1, 'BB']; console.log(arr2); |
2)对象操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
var obj1 = {name:'zs',age:18}
var obj2 = {
sex:'男' ,
...obj1 ,
weight:100
}
var obj3 = {
...obj1,
weight:200,
// 如果存在相同的属性,则后者把前者覆盖
name:'张三'
}
console.log(obj2);
console.log(obj3);
|
3)参数传递
1 2 3 4 5 6 7 8 |
function sum(a,b,c) {
console.log(a + b + c);
}
var arr = [1,2,3] ;
// 展开数组,依次把数据传递到函数的参数中
sum(...arr) ;
|
2、rest:收集
rest与spread是相对的操作,rest可以收集剩余的参数,形成可变参数的函数,与Java中的...类似。
1 2 3 4 5 6 7 8 9 10 |
function sum(a,...args) {
// 分别输出参数的值
console.log(a) ;
console.log(args);
}
// sum(1) ;
sum(1,2,3,4)
|
在JS中,函数内置的数组对象(arguments),也可以收集函数的参数值。它们的区别如下:
- rest只包含那些没有给出名称的参数,而arguments包含全部参数
- arguments对象不是数组,它是一个像是数组的对象,而rest是数组对象
- arguments是内置对象,默认存在于函数中,而rest必须使用
...显式声明
另外,rest的参数必须定义在函数的最后一个参数中;函数对象的length属性不计算rest参数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
function sum(a,b,...args) {
// 分别输出参数的值
console.log(a);
console.log(b);
console.log(args);
console.log(arguments instanceof Array) ;
console.log(args instanceof Array) ;
}
sum(1,2,3,4)
// 返回1,rest不在length属性的计算范围内
console.log(sum.length);
|
五、解构
解构(Destructuring)是ES6新增的一个赋值方法。按归对应的位置,从数组或对象中提取值,然后赋值给变量。使用解构将极大的方便从数组或对象中取值。
1、数组解构
语法:let|var|const [变量1,…,变量N] = 数组 ;
按顺序把数组的元素赋值给变量1,变量2,…
1 2 3 4 |
let [a, b, c] = [100, 200, 300]; console.log(a) // 100 console.log(b) // 200 console.log(c) // 300 |
如果变量数据与数组无数不匹配,多了忽略,少了变量赋值为undefined
1 2 3 4 5 6 7 8 9 |
let [a, b, c] = [100, 200]; let [aa,bb] = [10,20,30] console.log(a) // 100 console.log(b) // 200 console.log(c) // undefined console.log(a) // 10 console.log(b) // 20 |
特殊使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
// 按顺序对应赋值,逗号表示跳过
let [a, , b, , c] = [100, 200, 300, 400, 500, 600];
console.log(a) // 100
console.log(b) // 300
console.log(c) // 500
// 数组中的元素可以是任意类型的数据
let [aa, bb, cc, dd] = ['AA', [true, 20], { name: 'zs' }, function () { console.log('函数'); }];
console.log(aa);
console.log(bb);
console.log(cc);
dd() ;
// 结合rest使用
let [aaa,...bbb] = [1,2,3,4,5] ;
console.log(aaa);
console.log(bbb);
|
2、对象解构
语法:let|var|const {属性名1[:别名],…,属性名N[:别名]} = 对象 ;
按顺序把对象的属性值赋值给属性名1,属性2,…。或赋值给相应的别名变量。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
let person = {name:'zs',age:18}
// 变量名称必须与对象的属性名称一致
let {name,age} = person ;
console.log(name); // zs
console.log(age); // 18
// 可以把获取的属性值赋值给指定的变量名称
let {name:xingMing,age:nianLing} = person ;
console.log(xingMing); // zs
console.log(nianLing); // 18
// 允许重复使用属性,允许使用表达式,可以把取出的值赋值给对象的属性
// 当不是赋值操作时,必须使用括号,转换为表达式
let obj = {} ;
({name:obj.x,age:obj['user_'+'age'],name:obj.z} = person) ;
console.log(obj); // {x: "zs", user_age: 18, z: "zs"}
//结合rest的使用
let {username,...attrs} = {username:'小四',age:20,sex:'女'}
console.log(username); // 小四
console.log(attrs); // {age:20,sex:'女'}
|
3、解构函数参数
解构赋值也可以结合函数参数进行使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
// 数组解构作为参数
function sum1( [m,n] ) {
return m + n ;
}
// 对象解构作为参数
function sum2( {m,num:n} ) {
return m + n ;
}
// 对象解构结合默认值作为参数
function sum3({m=100},{n=300}) {
return m + n ;
}
console.log( sum1([1,2]) );
console.log( sum2({m:10,num:20}) );
console.log(sum3({},{}));
console.log(sum3({m:300},{}));
console.log(sum3({},{n:500}));
console.log(sum3({m:500},{}));
|
六、箭头函数
箭头函数(Arrow Function)简化了函数的定义,它不仅是语法糖,同时也更正了this复杂不可控的情况。
1、语法
1 2 3 4 5 6 7 8 |
// ():代表是函数;
// =>:必须要的符号,指向哪一个代码块;
// {}:函数体
([参数列表]) => { 函数体 }
// 代表把一个函数赋值给fn
const fn = () => {}
|
2、特点
1)省略大括号
函数体中只有一句代码,且代码的执行结果就是返回值,可以省略大括号
1 2 3 4 5 6 |
function sum(num1, num2) {
return num1 + num2;
}
// ES6写法
const sum = (num1, num2) => num1 + num2 ;
|
2)省略小括号
如果形参只有一个,可以省略小括号
1 2 3 4 5 6 7 |
function add (n) {
return ++n ;
}
// ES6写法
// const add = (n) => ++n ;
const add = n => ++n ;
|
3)this关键字
箭头函数不动态绑定this关键字,箭头函数中的this,指向的是函数定义位置的上下文this(父作用域)
由于箭头函数不绑定this, 它会捕获其所在(即定义的位置)上下文的this值, 作为自己的this值
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 |
<button>点我看看</button>
<script>
var obj = {
name:'测试',
fun1 : function() {
// 这里的this,根据调用此方法动态绑定的,由其调用的上下文決定this关键字的指向
// 即,谁调用fun1方法,this表示的就是谁
console.log(this);
} ,
fun2 : () => {
// 箭头函数不动态绑定this,在这里,fun2函数定义的位置上下文中,当前对象指定的是window对象
console.log(this);
}
}
// this指的是obj对象
obj.fun1() ;
// this指的是window对象
obj.fun2() ;
// this指的是button对象
document.querySelector("button").onclick = obj.fun1 ;
// 注意:不能通过call方法的调用,改变this的指向
// obj.fun2.call({name:'zs'}) ;
</script>
|
4)改变this的指向
- 不能通过call、apply或bind调用方法,从而改变箭头方法中this的指向
- 可以通过改变箭头函数定义上下文的this对象,从而改变箭头方法中的this指向
1 2 3 |
// 接上例
// this仍然是window对象
obj.fun2.call({name:'zs'}) ;
|
思考一
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
const user = { name: '张三' }
function fun() {
// console.log(this);
return () => {
// 这里的this指向的是什么对象?
console.log(this);
}
}
// 通过call调用fun方法,并重新设置this的引用(指向)
var reFun = fun.call(user) ;
reFun() ;
|
思考二
1 2 3 4 5 6 7 8 9 10 11 12 13 |
var num = 100;
var obj = {
num : 200,
fun : () => {
console.log(this.num)
}
}
// 输出的结果为?
// 箭头函数this指向的是被声明的作用域里面,而对象没有作用域的,
// 所以箭头函数虽然在对象中被定义,但是this指向的是全局作用域
obj.fun();
|
5)arguments、caller、callee在箭头函数中不存在
6)prototype属性在箭头函数中不存在
7)箭头函数不能作为构造器(构造方法)
8)不可以使用yield命令,因为箭头函数不能用作Generator函数。
七、循环
1、for…in
for...in语句以任意顺序遍历一个对象的除Symbol以外的可枚举属性。
1)语法
1 2 3 |
for (variable in object) {
statement
}
|
variable在每次迭代时,variable会被赋值为不同的属性名。
object非Symbol类型的可枚举属性被迭代的对象。
2)循环遍历数组
1 2 3 4 5 6 7 |
var arr = [100,200,300] ;
// 在循环遍历数组时,迭代变量指向的是数组的下标索引
for(let i in arr) {
// console.log(i)
console.log(arr[i])
}
|
3)循环遍历对象
1 2 3 4 5 6 7 8 9 10 |
var obj = {name:'zs',age:18,sex:'男'}
// 在循环遍历对象时,迭代变量指向的是对象的属性
for(let attr in obj) {
// 输出属性名称:name,age,sex
// console.log(attr)
// 获取对象属性对应的值
console.log(obj[attr])
}
|
说明:for ... in是为遍历对象属性而构建的,不建议与数组一起使用。
2、for…of
for...of语句在可迭代对象(包括 Array,Map,Set,String,TypedArray,arguments 对象等等)上创建一个迭代循环,调用自定义迭代钩子,并为每个不同属性的值执行语句。
1)语法
1 2 3 |
for (variable of iterable) {
//statements
}
|
variable在每次迭代中,将不同属性的值分配给变量。
iterable被迭代枚举其属性的对象。
2)迭代数组
1 2 3 4 5 6 7 8 9 10 |
let iterable = [10, 20, 30];
for (let value of iterable) {
value += 1;
console.log(value);
}
// 11
// 21
// 31
|
3)迭代字符串
1 2 3 4 5 6 7 |
let iterable = "hello";
for (let value of iterable) {
console.log(value.toUpperCase());
}
// HELLO
|
4)迭代arguments对象
1 2 3 4 5 6 7 8 9 |
(function() {
for (let argument of arguments) {
console.log(argument);
}
})(1, true, 'hello');
// 1
// true
// hello
|
5)迭代Map
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
let iterable = new Map([["a", 1], ["b", 2], ["c", 3]]);
// let iterable = [["a", 1], ["b", 2], ["c", 3]] ;
for (let entry of iterable) {
console.log(entry);
}
// ["a", 1]
// ["b", 2]
// ["c", 3]
for (let [key, value] of iterable) {
console.log(key + '=' + value);
}
// a=1
// b=2
// c=3
|
3、forEach
forEach() 方法对数组的每个元素执行一次给定的函数。
语法
1 |
arr.forEach(callback(currentValue [, index [, array]])[, thisArg]) |
callback为数组中每个元素执行的函数,该函数接收一至三个参数:
currentValue数组中正在处理的当前元素。
index可选数组中正在处理的当前元素的索引。
array可选forEach()方法正在操作的数组。thisArg可选可选参数。当执行回调函数
callback时,用作this的值。
1 2 3 4 5 6 7 |
var arr = ['你好', '我好', '大家好'];
arr.forEach(function (v,i,a) {
console.log(v);
console.log(i);
console.log(a);
});
|
注意:forEach不能停止或退出
4、filter
过滤数组
1 2 3 4 5 6 7 8 9 10 11 12 |
var arr = [11, 22, 33, 44, 55, 66];
// 参数同forEach
var newArr = arr.filter(function (value, index, array) {
// 参数一是:数组元素
// 参数二是:数组元素的索引
// 参数三是:当前的数组
return value >= 50;
});
// [55,66],返回经过滤后的一个新数组
console.log(newArr);
|
计算购物车中,选中商品的总价
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 |
// 定义数组,判断数组各元素中的status属性是否都为 true
const arr = [
{ id:1, name:'苹果', status:true, price:10, count:1 },
{ id:2, name:'雪梨', status:false, price:20, count:2 },
{ id:3, name:'草莓', status:true, price:30, count:3 }
} ;
// 过滤选中的商品
// arr.filter(item=>item.status) ;
// 方法一
// 定义累加变量
let total = 0 ;
arr.filter(item=>item.status).forEach(item=>{
total = total + item.price * item.count ;
}) ;
console.log(total);
// 方法二
// arr.filter(item=>item.status).reduce((累加变量,循环的当前元素)=>{},初始值);
arr.filter(item=>item.status).reduce((total,item)=>{
total = total + item.price * item.count ;
return total ;
},0);
// 简化
// arr.filter(item=>item.status).reduce((total,item)=>total = total + item.price * item.count,0);
|
5、some
**查找数组中是否有满足条件的元素 **
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
var arr = [11, 22, 33, 44, 55, 66];
// 参数同forEach
// 查找数组中是否有满足条件的元素
// 如果不满足,则返回false
// 如果查找到满足条件的一个元素,则返回true
var bl = arr.some(function (value, index, array) {
// 参数一是:数组元素
// 参数二是:数组元素的索引
// 参数三是:当前的数组
return value >= 50;
});
// true,返回是否存在满足条件的元素
console.log(bl);
|
注意:区别forEach
- forEach循环:一旦循环开始,不能终止循环
- som循环:返回true时,终止循环
6、every
1 2 3 4 5 6 7 8 9 10 11 12 13 |
// 定义数组,判断数组各元素中的status属性是否都为 true
const arr = [
{ id:1, name:'苹果', status:true },
{ id:2, name:'雪梨', status:true },
{ id:3, name:'草莓', status:true }
}
const r = arr.every(function(item){
return item.status== true;
}) ;
//const r = arr.every(item => item.status) ;
console.log(r);
|
八、Symbol
symbol 是一种基本数据类型 (primitive data type)。Symbol()函数会返回symbol类型的值,该类型具有静态属性和静态方法。它的静态属性会暴露几个内建的成员对象;它的静态方法会暴露全局的symbol注册,且类似于内建对象类,但作为构造函数来说它并不完整,因为它不支持语法:”new Symbol()“。
Symbol的主要目的是为了解决属性名冲突的问题,每个从Symbol()返回的symbol值都是唯一的。symbol值能作为对象属性的唯一标识,防止冲突。如果一个对象已经使用了某个属性名,再定义就会覆盖。
ES6 引入了一种新的原始数据类型 Symbol ,表示独一无二的值,最大的用法是用来定义对象的唯一属性名。
ES6 数据类型除了 Number 、 String 、 Boolean 、 Objec t、 null 和 undefined ,还新增了 Symbol 。
1、创建Symbol
语法
1 2 3 |
// 使用Symbol函数返回Symbol类型的值 // description:可选的,字符串或数字类型。对symbol的描述,可用于调试但不是访问symbol本身。 Symbol([description]) |
创建
1 2 3 4 5 6 |
var sym1 = Symbol();
var sym2 = Symbol('name');
var sym3 = Symbol(123456);
console.log(sym1) ;
console.log(typeof sym1) ;
|
Symbol()函数返回的值都是唯一的,哪怕描述字符串相同也是不一样的
1 2 3 4 5 |
const sym1 = Symbol('name') ;
const sym2 = Symbol('name') ;
console.log(sym1 == sym2) ;
console.log(sym1 === sym2) ;
|
使用Symbol类型数据作为对象属性名时,必须加上中括号,如[symbole]
1 2 3 4 5 6 7 8 9 10 11 |
const sym_name = Symbol('name') ;
const sym_age = Symbol('age') ;
var obj = {
[sym_name] : 'zs' ,
[sym_age] : 18
}
console.log(obj[sym_name]) ;
console.log(obj[sym_age]) ;
console.log(obj) ;
|
其它操作
1 2 3 4 5 6 7 8 9 10 11 12 13 |
var sym = Symbol('name') ;
// 输出Symbol的描述信息
console.log(sym.description);
// boolean运算,返回false
console.log(!sym);
// 转换为字符串输出
console.log(sym.toString());
// 错误,进行加法运算,无法自动转换为字符串
console.log(sym + '-');
|
2、全局Symbol
1)Symbol.for
Symbol.for(key) 方法会根据给定的键 key,来从运行时的 symbol 注册表中找到对应的 symbol,如果找到了,则返回它,否则,新建一个与该键关联的 symbol,并放入全局 symbol 注册表中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// 创建一个全局 Symbol
let name = Symbol.for('name') ;
// 创建一个对象,使用Symbol作为属性
let user = {
[name] : 'tom'
} ;
// 在全局注册表中,获取Symbol
let uname = Symbol.for('name') ;
// 输出对象的属性
console.log(user[uname]);
// 判断从全局注册中获取的Symbol类型数据是否一样
console.log(name === uname);
|
2)Symbol.keyFor
**Symbol.keyFor(sym) ** 方法用来获取全局symbol 注册表中与某个 symbol 关联的键。
如果全局注册表中查找到该symbol,则返回该symbol的key值,返回值为字符串类型。否则返回undefined
1 2 3 4 5 6 7 8 9 10 11 12 |
// 创建一个全局 Symbol
var globalSym = Symbol.for("foo");
// 'foo'
Symbol.keyFor(globalSym);
// string
Symbol.keyFor(typeof globalSym);
// 非全部Symbol
var localSym = Symbol();
// undefined,
Symbol.keyFor(localSym);
|
3、应用
实现枚举
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// 定义一个对象,实现枚举的作用
let TYPES = {
HIGH : Symbol() ,
MIDDLE : Symbol() ,
LOW : Symbol()
} ;
// 三个属性使用了Symbol类型数据,虽然都一样,但返回的是唯一的值
console.log(TYPES.HIGH);
console.log(TYPES.HIGH==TYPES.MIDDLE);
// 逻辑判断
var type = TYPES.HIGH ;
if(type == TYPES.HIGH) {
console.log('高');
}
|
九、模板字符串
在ES6中新增的创建字符串的方式,使用反引号定义。在模板字符串,使用${变量名}的形式访问变量的值。在大括号中也支持表达式,大大提高程序的灵活性。
1 2 3 4 5 6 7 8 9 |
let obj = {
name : 'zs' ,
age : 18 ,
sex : '男'
}
let info = `姓名:${obj.name},年龄:${obj.age + 1},性别:${obj.sex}` ;
console.log(info) ;
|
在模板字符串中可以调用函数
1 2 3 4 5 6 7 8 9 10 11 |
const sayHello = function (name) {
return '你好,' + name;
};
let info = `${sayHello('李四')}. I LOVE YOU`;
console.log(info);
// 注意可读性
console.log(`${(function(v){return v})('hello,world')}`);
|