从零开始学习前端JAVASCRIPT — 10JavaScript基础ES6(ECMAScript6.0)

Posted 大仲马

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从零开始学习前端JAVASCRIPT — 10JavaScript基础ES6(ECMAScript6.0)相关的知识,希望对你有一定的参考价值。

ECMAScript 6.0(简称ES6)是javascript语言的下一代标准,已经在2015年6月正式发布了。它的目标,是使得JavaScript语言可以用来编写复杂的大型应用程序,成为企业级开发语言。各大浏览器的最新版本,随着时间的推移,支持度已经越来越高了,ES6的大部分特性都实现了。那么也就意味着低版本浏览器是不支持ES6的。

1:let/const 声明变量关键字

1)let关键字特点

  • let声明的变量只在所在的块级作用域内有效(块级作用域特点:不受外部环境影响和内部代码块影响)
  • js预解析时不存在变量的提升定义,存在暂时性死区,声明语句位置后使用,否则异常 
  • 暂时性死区(释义:let声明的变量进入所在作用域已存在,未声明前使用报错,受封闭作用域,不受外部的影响) 
  • 不允许重复声明,重复声明异常  
<!doctype html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>ES6</title>
	<script type="text/javascript">
		// 作用域:全局作用域、函数作用域
		// ES6新增了一个 块级作用域
		var varname="王五"
		{
			console.log(varname);//输出王五
			var varname = \'张三\';
			var varname = \'赵四\';
			{
				var varname="刘能"
				console.log(varname)//输出刘能
			}
		}
		console.log(varname)//输出刘能
		console.log("————————————————我是分割线————————————————")
		console.log("——————var关键字和let关键字的作用比对——————")
		console.log("————————————————我是分割线————————————————")
		let letname="李四"
		{
			//console.log(letname);
			//取消注释直接异常提示未被定义,let声明的变量不存在变量的提升定义
			let letname = \'张三\';
			//let letname = \'赵四\';
			//取消注释直接异常提示已被声明
			{
				let letname="刘能";
				console.log(letname);
			}
			console.log(letname);
			//输出张三块暂时性的死区,块级作用域不会受外部、内部的影响
		}
		console.log(letname);//输出李四
	</script>
</head>
<body>
	
</body>
</html>

  

let的使用 

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>ES6</title>
    <style type="text/css">
        *{
            margin: 0;
            padding: 0;
        }
        ul,ol{
            list-style: none;
        }
        #list{
            width: 50%;
            margin: 100px auto;
            border: 1px solid #f3f3f3;
        }
        #list li{
            text-align: center;
            border-bottom: 1px dashed #f3f3f3;
            line-height: 50px;
            font-size: 26px;
            letter-spacing: 10px;
        }
    </style>
</head>
<body>
    <ul id="list">
        <li>我是第1行,我很皮</li>
        <li>我是第2行,我很皮</li>
        <li>我是第3行,我很皮</li>
        <li>我是第4行,我很皮</li>
        <li>我是第5行,我很皮</li>
    </ul>
</body>
<script type="text/javascript">
    var oList=document.getElementById(\'list\');
    var oLi=oList.children;
    //var定义变量实现各行换色
/*    for (var i = 0; i < oLi.length; i++) {
        i % 2 === 0?oLi[i].style.background = \'#eee\':oLi[i].style.background = \'#fff\'
        oLi[i].index=i;//给每个元素添加属性index记录下标
        oLi[i].onmouseover=function() {
            this.style.background = \'#e33\'
        }
        oLi[i].onmouseout=function() {
            //不直接使用i的原因说明:i是全局变量事件只有在调用的时候执行,触发事件的时候,for循环已经执行完毕,则i的值则为29,保持不变
            //通过添加的属性判断下标的奇偶设置背景色
            this.index % 2 === 0?this.style.background = \'#eee\':this.style.background = \'#fff\'
        }
    }*/
    //使用let关键字进行定义变量利用其的块级作用域进行实现,每次循环都相当于重新创建变量i
        for (let i = 0; i < oLi.length; i++) {
        i % 2 === 0?oLi[i].style.background = \'#eee\':oLi[i].style.background = \'#fff\'
        oLi[i].onmouseover=function() {
            this.style.background = \'#e33\'
        }
        oLi[i].onmouseout=function() {
            //不直接使用i的原因说明:i是全局变量事件只有在调用的时候执行,触发事件的时候,for循环已经执行完毕,则i的值则为29,保持不变
            //通过添加的属性判断下标的奇偶设置背景色
            i % 2 === 0?this.style.background = \'#eee\':this.style.background = \'#fff\'
        }
    }

</script>
</html>
let的使用

  

2)const关键字特点

  • const定义的常量是不允许改变值,声明和赋值必须放在一起,只声明不赋值,就会报错 
const a = 5;
a = 6;
console.log(a);
  • 拥有和let关键字的所有的特点(参考let属性的说明 
  • const常量定义关键字定义的复合类型的变量,变量名不指向存储的数据,指向数据的存储地址,const定义的变量控制的是指向的地址不变,并不是该地址指向的数据不变 
// 特殊的场景
const a = {name: \'赵四\'};
a.name = \'刘能\';
console.log(a);

  

3)const命令和let命令的区别

1)const关键字定义的是常量,值无法改变,经常使用在值不变的情况下,如定义圆周率等。let定义的变量,块级作用域。

2)顶层对象,在浏览器环境指的是window对象,在Node指的是global对象。ES5之中,顶层对象的属性与全局变量是等价的

ES6全局变量将逐步与顶层对象的属性脱钩:

let命令、const命令、class命令声明的全局变量,不属于顶层对象的属性。

var命令和function命令声明的全局变量,依旧是顶层对象的属性(保证兼容性)

// 顶层对象属性
var vara = 4;
console.log(window.vara);
console.log("—————————分割线—————————")
let leta = 4;
console.log(window.leta);

   


2:变量结构赋值

ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)

“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值。

 (各类的变量解构赋值在代码中说明,请祥看注释)

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>ES6</title>
</head>
<body>
</body>
<script type="text/javascript">
    //——————————————— 变量解构赋值————————————//
    // 从数组中提取值
    var arr = [\'张三\', \'赵四\', \'王五\'];
    //ES6之前的写法
    var a = arr[0];
    var b = arr[1];
    var c = arr[2];
    console.log(a, b, c);
    /*ES6变量解构赋值(左右结构对称,模式相同,就会把左边的变量赋予对应的值)*/
    //数组的解构
    //——————————————— 解构赋值————————————//    
    console.log("变量解构赋值:");
    var [a, b, c] = arr;
    console.log(a, b, c);
    //——————————————— 解构赋值————————————//
    console.log("数组的解构:")
    var [[a], [[b]], [c], [[[d]]]] = [[1], [[2]], [3], [[[4]]]];
    console.log(a,b,c,d)
    //不完全解构
    var [a, b, c, d] = [1, 2, 3];
    console.log(a,b,c,d)//如果结构不成功,则返回undefined
    var [a,[b],c]=[1,[2,3],4]
    console.log(a,b,c)
    /*不完全解构:等号左边的模式,只匹配一部分的等号右边的数组。这种情况下,解构依然可以成功*/
    // 指定默认值
    console.log("指定默认值");
    var [a, b, c, d = 0] = [1, 2, 3];
    console.log(a, b, c, d);
    /*ES6内部使用严格相等运算符(===),判断一个位置是否有值。若一个数组成员不严格等于undefined,默认值是不会生效的*/
    var [x=1]=[undefined];
    console.log(x);//输出为1
    var [x=1]=[null];
    console.log(x);//输出为null

    //——————————————— 解构赋值————————————//
    // ... 扩展运算符
    console.log("扩展运算符:")
    var [a, b, ...c] = [1, 2, 3, 4]; //...只能放到最后一个参数
    console.log(a,b,c)//输出为1,2,[3,4]
    // var [...a, b, c] = [1, 2, 3, 4]; // 不可以
    var [,,c] = [1, 2, 3];
    console.log(c)//输出为3

    //——————————————— 解构赋值————————————//
    // 对象的解构赋值
    console.log("对象的解构赋值:")
    var {name: name, age: age} = {name: \'赵四\', \'age\': 28};
    console.log(name, age);
    var {name, age} = {name: \'赵四\', \'age\': 28};
    console.log(name, age);
    //对象的属性没有次序,变量必须与属性同名,才能取到正确的值
    var {age, name} = {name: \'赵四\', \'age\': 28};
    console.log(name, age);
    var {age, name, sex=\'由于某种原因未知\'} = {name: \'赵四\', \'age\': 28};
    console.log(name, age, sex);

    /*对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者。*/
    var {aniname:fname,aniage:fage}={aniname:"动物名称",aniage:"动物年龄"}
    console.log(fname,fage)
    /*    对象的默认值设置:默认值生效的条件是,对象的属性值严格等于undefined。如果要已经声明的变量用于
    解构赋值,需将代码块包到小括号内,否则JavaScript引擎会将{x}理解成一个代码块,从而发生语法错误。*/
    let carname;
    /*{carname}={carname:"BMW"}//报错*/
    ({carname}={carname:"BMW"})
    console.log(carname)

    //——————————————— 解构赋值————————————//
    // 函数的解构传参
    console.log("函数的返回值解构:");
    function introduce({name, age=\'保密\', sex}) {
        console.log(\'我叫\' + name + \';今年\' + age + \';性别\' + sex);
    }
    introduce({
        sex: \'boy\',
        name: \'赵四\'
    });

    //——————————————— 解构赋值————————————//
    //函数的返回值解构
    console.log("函数的返回值解构:");
    function profile() {
        return {
            newname: \'张三\',
            height: 168,
            newsex: \'\'
        };
    }
    let {height, newname, newsex} = profile();
    console.log(height, newname, newsex);

    /*解构赋值不仅适用于var命令,也适用于let和const命令。*/
</script>
</html>

 

 

 

    


3:字符串扩展

1)ES6字符串新增的遍历器接口(for...of)

var str = \'good good study day day up!\';
for(let v of str) {
	console.log(v);
}

  2)ES6字符串新增的方法(includes(),startsWith(),endsWith(),repeat())

<!doctype html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>ES6</title>
</head>
<body>
</body>
	<script type="text/javascript">
	var str = \'good good study day day up!\';
	//第一个参数指定查找的字符,第二个参数指定开始查找的位置
	console.log(str.includes(\'good\', 15));
	//第一个参数指定查找的字符,第二个参数指定开始查找的位置
	console.log(str.startsWith(\' \', 15));
	//第一个参数指定查找的字符,第二个参数指定结束位置的字符
	console.log(str.endsWith(\'y\', 15));
	//repeat方法返回一个新字符串,表示将原字符串重复n次。
/*	includes():返回布尔值,表示是否找到了参数字符串。
	startsWith():返回布尔值,表示参数字符串是否在源字符串的头部。
	endsWith():返回布尔值,表示参数字符串是否在源字符串的尾部。*/
	console.log(\'x\'.repeat(10) + \'o\'.repeat(10)); 
	//参数如果是小数,会被向下取整。
	console.log(\'x\'.repeat(10.1) + \'o\'.repeat(10.9)); 
	//参数NaN等同于0。
	console.log(\'x\'.repeat(NaN) + \'o\'.repeat(10)); 
	//如果repeat的参数是字符串,则会先转换成数字。
	console.log(\'x\'.repeat("10") + \'o\'.repeat(10)); 
</script>
</html>

  

3)ES6模板字符串

模板字符串(template string)是增强版的字符串,用反引号(`)标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。如果在模板字符串中需要使用反引号,则前面要用反斜杠转义。模板字符串中嵌入变量,需要将变量名写在${}之中。模板字符串表示多行字符串,所有的空格和缩进都会被保留在输出之中。

/*oLi.innerHTML=\'<a href="javascript:;" class="abtn" data-id="\'+aGoodList[i].id+\'">删除该商品</a><img src="\'+aGoodList[i].src+\'" /><div class="title">\'+aGoodList[i].title+\'</div><div class="price">¥\'+aGoodList[i].price+\'/个|数量\'+aGoodList[i].amount+\'</div>\';*/
/*使用模板字符换拼接该字符串改写上面的字符串*/
oLi.innerHTML=`
<a href="javascript:;" class="abtn" data-id="${aGoodList[i].id}">
删除该商品
</a>
<img src="${aGoodList[i].src}" />
<div class="title">
    ${aGoodList[i].title}
</div>
<div class="price">
    ¥${aGoodList[i].price}/个|数量${aGoodList[i].amount}
</div>`; 

 

 

 

 


4:数值扩展

1)二进制和八进制的表示法

 ES6提供了二进制和八进制数值的新的写法,分别用前缀0b(或0B)和0o(或0O)表示

console.log(0o123);
console.log(0b1010101);

  

\'use strict\';
// console.log(0123);//此处报错
console.log(0o123);

  0b和0o前缀的字符串数值转为十进制,要使用Number方法。

console.log(Number(0b0111));//7
console.log(Number(0o123));//83

2)Number对象的扩展 Number.isNaN()、Number.parseInt()、Number.parseFloat()

1>Number.isNaN()

ES6在Number对象上,新提供了Number.isNaN()方法。Number.isNaN()用来检查一个值是否为NaN。

console.log(isNaN(NaN));//NaN是NaN类型  返回true
console.log(Number.isNaN(NaN));//NaN的数据类型是number类型返回true

与传统的全局方法isNaN()的区别在于,传统方法先调用Number()将非数值的值转为数值,再进行判断,而该新方法只对数值有效,非数值一律返回false。

// isNaN和Number.isNaN的区别
console.log(isNaN(\'12px\'));//非数值先转数值类型在判断
console.log(Number.isNaN(\'12px\'));//如果是非数值类型,直接输出false

2>Number.parseInt()(逐步减少全局性方法,使得语言逐步模块化)

console.log(parseInt("12px"))
console.log(Number.parseInt("12px"))//除了将方法挂载到Number身上无任何差别

  3>Number.parseFloat()(逐步减少全局性方法,使得语言逐步模块化)

console.log(parseFloat("12px"))
console.log(Number.parseFloat("12px"))//挂载到Number身上无任何差别

  3)Math对象的扩展 Math.trunc(),Math.sign(),Math.cbrt(),Math.hypot()

1>Math.trunc()去除小数向下取整,非数值Math.trunc内部使用Number方法将其先转为数值。对于空值和无法截取整数的值,返回NaN。

2>Math.sign():用来判断一个数到底是正数、负数、还是零。返回值:参数为正数,返回1;参数为负数,返回-1;参数为0,返回0;参数为-0,返回-0;其他值,返回NaN。

3>Math.cbrt()用于计算一个数的立方根。非数值,Math.cbrt方法内部也是先使用Number方法将其转为数值。

4>Math.hypot()返回所有参数的平方和的平方根。非数值,Math.hypot方法会将其转为数值。只要有一个参数无法转为数值,就会返回NaN。

// Math对象的扩展
console.log("Math.trunc方法:")
console.log(Math.trunc(4.9));
console.log("Math.sign方法:")
console.log(Math.sign(4.9));
console.log(Math.sign(-4.9));
console.log(Math.sign(0));
console.log(Math.sign(+0));
console.log(Math.sign(-0));
console.log("Math.cbrt方法:")
console.log(Math.cbrt(8));
console.log("Math.hypot方法:")
console.log(Math.hypot(3,4));

     


5:数组扩展 

  • Array.from()用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象。
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>ES6</title>
</head>
<body>
    <ul id="list">
        <li>这是第1个LI</li>
        <li>这是第2个LI</li>
        <li>这是第3个LI</li>
        <li>这是第4个LI</li>
        <li>这是第5个LI</li>
        <li>这是第6个LI</li>
        <li>这是第7个LI</li>
        <li>这是第8个LI</li>
        <li>这是第9个LI</li>
        <li>这是第10个LI</li>
    </ul>
</body>
<script type="text/javascript">
    var oList = document.getElementById(\'list\');
    console.log("未转换前输出OList是否数组的结果:")
    console.log(Array.isArray(oList.children));//伪数组
    var aLi = oList.children;
    for(var i = 0; i < aLi.length; i++) {
        aLi[i].onclick = function () {
            this.style.background = \'red\';
        }
    }
    console.log("已转换后输出OList是否数组的结果:")
    var aLi = Array.from(oList.children);
    console.log(Array.isArray(aLi));
    aLi.forEach(function (v) {
        console.log(v);
    });
</script>
</html>

  • find()和findIndex() 数组的实例方法  

find(参数是回调函数),用于找出第一个符合条件的数组成员然后返回该成员。如果没有符合条件的成员,则返回undefined。

findIndex(参数是回调函数)用于找出第一个符合条件的数组成员然后返回该成员索引,如果所有成员都不符合条件,则返回-1

 

var arr = [1, 4, 7, 9];

var result = arr.find(function (v) {
	if(v > 5) {
		return true;
	} else {
		return false;
	}
});
console.log(result);


var result = arr.findIndex(function (v) {
	if(v > 5) {
		return true;
	} else {
		return false;
	}
});

console.log(result);

  


6:函数扩展

 1)函数参数默认值 

 

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>ES6</title>
</head>
<body>
</body>
<script type="text/javascript">
    //es6通过判断参数给定值
    function introduce(name, age, sex ) {
        // let sex;//不能重复定义,参数变量是默认声明的,所以不能用let或const再次声明。
        //方式一:
        if(typeof(sex)===undefined){
            sex=\'unknown\'
        }
        //方式二:
        //sex = sex || \'unknown\';
        //缺点参数sex对应的布尔值为false,则该赋值不起作用。
        console.log(`my name is ${name}, my age is ${age}, my sex is ${sex}`);
    }
    introduce(\'赵四\',false);
    //es6直接传参赋默认值
    function introducees6(name, age=27, sex="unknown" ) {
        console.log(`my name is ${name}, my age is ${age}, my sex is ${sex}`);
    }
    introducees6(\'赵四\');
</script>
</html>

 

2)rest参数

 ES6引入rest参数(形式为“...变量名”),用于获取函数的多余参数,这样就不需要使用arguments对象了。rest参数搭配的变量是一个数组,该变量将多余的参数放入数组中。rest参数之后不能再有其他参数(即只能是最后一个参数),否则会报错。   

<!doctype html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>ES6</title>
</head>
<body>
</body>
<script type="text/javascript">
	function sum(...args) {
	console.log(Array.isArray(arguments));//伪数组
	console.log(Array.isArray(args));//数组类型
	return args.reduce(function (prev, current) {
		return prev + current;
	});
}
console.log(sum(1,4,7,9,10, 10, 23, 34));
</script>
</html>

  

3)扩展运算符:扩展运算符(spread)是三个点(...)。它好比rest参数的逆运算,将一个数组转为用逗号分隔的参数序列。

console.log(...[1, 4, 7]);

  

扩展运算符的应用:

1>合并数组:扩展运算符提供了数组合并的新写法。 

var a = [1, 4, 7], b = [2, 5, 8];
console.log("ES6之前合并数组的方法:")
var result = a.concat(b);
console.log(result)
console.log("ES6扩展运算符合并数组的方法:")
var result = [...a, ...b];
console.log(result)

   

2>与解构赋值结合:扩展运算符可以与解构赋值结合起来,用于生成数组。如果将扩展运算符用于数组赋值,只能放在参数的最后一位,否则会报错。 

var [a, b, ...c] = [1, 4, 7, 9, 10];
console.log(c)

   

3:字符串:扩展运算符还可以将字符串转为真正的数组。

var arr = [...\'hello world!\'];
console.log(arr);

     

严格模式:

从ES5开始,函数内部可以设定为严格模式。《ECMAScript 2016标准》做了一点修改,规定只要函数参数使用了默认值、解构赋值、或者扩展运算符,那么函数内部就不能显式设定为严格模式,否则会报错。

规定原因:函数内部的严格模式,同时适用于函数体代码和函数参数代码。但是,函数执行的时候,先执行函数参数代码,然后再执行函数体代码。这样就有一个不合理的地方,只有从函数体代码之中,才能知道参数代码是否应该以严格模式执行,但是参数代码却应该先于函数体代码执行。

 

箭头函数:ES6允许使用“箭头”(=>)定义函数。 

// 使用箭头的方式创建函数

//箭头函数不用加function关键字定义函数,也不用定义函数名
//代码块部分多于一条语句,就要使用大括号将它们括起来,并且使用return语句返回。
var sumone = (a, b) =>{
	a++;
	b++;
	return a + b;
};
console.log(sumone(4,5));//返回值:11


var sumone = (a, b) =>a + b;//单条语句可以省略花括号和return关键字
console.log(sumone(4,5));//输出的值:9


var sumtwo = a => a + 4;//单个惨数可以省略小括号
console.log(sumtwo(4));//输出的值:8


var sumthree =()=> 4+0;//无参数可以直接写括号
console.log(sumthree());//输出的值:4


//大括号代码块的标识,所箭头函数直接返回一个对象,必须在对象外面加上括号
var sumfour=(name)=>({name:name,age:25});
console.log(sumfour("赵四"));//{name: "赵四", age: 25}

  

  this的指向

 

// this在箭头函数中的使用

/*
	1:在普通函数中,this指向的是window,在严格模式下,this指向的是undefined
	2:在方法内,this指向的是方法的拥有者
	3:在箭头函数内,this指向的是创建箭头函数时所在的环境中this指向的值。
*/
var title = \'我是标题一\';
var obj = {
	title: \'我是标题二\',
	introduce: function () {
		console.log("1"+this.title);
		//方法内this指向拥有者obj,则值为1我是标题二
		var title="我是标题三"
		function introduce() {
			console.log("2"+this.title);
			//普通函数this指向window,则值为2我是标题一
		}
		introduce();
		var introduce = () => { console.log("3"+this.title) };
		//箭头函数this指向所在环境obj方法introduce中this,则值为3我是标题二
		introduce();
	}
};
obj.introduce();

  

注意点:

1:函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。(this对象的指向是可变的,但是在箭头函数中,它是固定的。

2:不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。

3:不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用Rest参数代替。

4:不可以使用yield命令,因此箭头函数不能用作Generator函数。


7:对象扩展

  1)对象的简写 

// 对象的扩展
var title = \'新华书店\';
var obj = {
	title: title,
	introduce: function () {
		console.log(this.title);
	}
};
console.log(obj);
var obj = {
	title,
	//属性的简写:ES6允许在对象直接写变量。属性名为变量名, 属性值为变量的值。
	introduce() {
		console.log(this.title);
	}
	//省略键名书写和定义方法的关键字function
};

console.log(obj);

   

2)对象的新增方法:Object.assign()

Object.assign()用于对象的合并,将源对象(source)的所有属性,复制到目标对象(target)

目标对象:Object.assign方法的第一个参数是目标对象

源  对  象:除了目标对象Object.assign方法的其他参数都是源对象

   注       意:(1)目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性。

              (2)Object.assign方法实行的是浅拷贝,而不是深拷贝。也就是说,如果源对象某个属性的值是对象,那么目标对象拷贝得到的是这个对象的引用。

// 合并对象
var obj1 = {a:1}, obj2= {b:2},obj3={b:3,c:4};
Object.assign(obj1, obj2, obj3);
console.log(obj1);//输出值:{a: 1, b: 3, c: 4}
//释义:obj1目标对象,obj2、obj3源对象
//		obj2和obj3的键名有重复后面的覆盖前面的值为3
var obj1 = {a:1}, obj2= {b:2},obj3={c:{d:5},e:6};
var obj4=Object.assign(obj1, obj2, obj3);
//Object.assign方法实行的是浅拷贝,而不是深拷贝。
console.log(obj1);
console.log(obj4.c);//Object.assign拷贝得到的是这个对象的引用
console.log(obj4.c.d);

  


 8:Set和Map结构

 1)Set结构

  1>Set结构创建:

var test=new Set();

 

   2>Set的赋值  

//先创建后赋值
var arr = [1, 2, 3, 4, 5]
var s = new Set();
for(var val of arr) {
   s.add(val);//add方法向Set结构添加元素
}