尚硅谷_ES5_6_7 笔记

已更新,见:https://blog.csdn.net/eartholtainanwan/article/details/107343858

ECMAScript入门介绍

1. 它是一种由ECMA组织(前身为欧洲计算机制造商协会)制定和发布的脚本语言规范
2. 而我们学的 JavaScript 是ECMA的实现, 但术语ECMAScript和JavaScript平时表达同一个意思
3. JS包含三个部分:
	1). ECMAScript(核心)
	2). 扩展==>浏览器端
		* BOM(浏览器对象模型)
		* DOM(文档对象模型)
	3). 扩展==>服务器端
		* Node
4. ES的几个重要版本
	* ES5 : 09年发布
	* ES6(ES2015) : 15年发布, 也称为ECMA2015
	* ES7(ES2016) : 16年发布, 也称为ECMA2016  (变化不大)
5. 扩展学习参考:
	1). ES5 
		http://www.zhangxinxu.com/wordpress/2012/01/introducing-ecmascript-5-1/
		http://www.ibm.com/developerworks/cn/web/wa-ecma262/
	2). ES6
		http://es6.ruanyifeng.com/
	3). ES7
		http://www.w3ctech.com/topic/1614

ES5

严格模式

1. 理解:
  * 除了正常运行模式(混杂模式),ES5添加了第二种运行模式:"严格模式"(strict mode)。
  * 顾名思义,这种模式使得Javascript在更严格的语法条件下运行
2. 目的/作用
  * 消除Javascript语法的一些不合理、不严谨之处,减少一些怪异行为
  * 消除代码运行的一些不安全之处,为代码的安全运行保驾护航
  * 为未来新版本的Javascript做好铺垫
3. 使用
  * 在全局或函数的第一条语句定义为: 'use strict';
  * 如果浏览器不支持, 只解析为一条简单的语句, 没有任何副作用
4. 语法和行为改变
  * 必须用var声明变量
  * 禁止自定义的函数中的this指向window
  * 创建eval作用域
  * 对象不能有重名的属性
  * 函数不能有重名的形参

JSON对象

1. JSON.stringify(obj/arr)
  * js对象(数组)转换为json对象(数组)
2. JSON.parse(json)
  * json对象(数组)转换为js对象(数组)

严格来说,只有JSON对象和JSON数组,没有“JSON字符串”这一概念。
JSON是数据传输的格式。如果服务器发送给我们的是普通的字符串,我们并不能直接使用,
  因为服务器发送的很可能是一长串数据,里面包含了多个数据,无法对数据进行分类。
  	因此我们需要的是JSON的对象和数组可以与原生的对象和数组进行转化,即,一一对应的关系。
  	  [对象(数组)都是用来保存数据的。]
<script type="text/javascript">
    var obj = {
        name : 'kobe',
        age : 39
    };
    obj = JSON.stringify(obj);
    console.log( typeof obj);//结果为:string
    obj = JSON.parse(obj);
    console.log(obj);//结果为:object
</script>

Object(对象)扩展

ES5给Object扩展了好一些静态方法, 常用的2个:
Object.create 和 Object.defineProperties

Object.create

Object.create(prototype, [descriptors])  (prototype是指定的原型,[descriptors]是相应的描述)
  * 作用: 以指定对象为原型创建新的对象
  * 为新的对象指定新的属性, 并对属性进行描述
    value: 指定值
    writable: 标识当前属性值是否是可修改的, 默认为false
    configurable: 标识当前属性是否可以被删除 默认为false
    enumerable: 标识当前属性是否能用for in 枚举 默认为false

先看以下代码

<script type="text/javascript">
	var obj = {username: 'damu' , age: 30}
	var obj1 = {};
	obj1 = Object.create(obj);
	console.log(obj1);
</script>

此时结果如下图所示:
在这里插入图片描述
此时,指定obj为原型,age和username是原型上的2个属性,因此是可继承的。


如果没有obj1 = Object.create(obj);_proto_中没有这两个属性,结果如下图所示:
在这里插入图片描述


当然,Object.create的作用不止如此。

<script type="text/javascript">
	var obj = {username: 'damu' , age: 30}
	var obj1 = {};
	obj1 = Object.create(obj , {
		sex: {
			value: '男'
		}
	});
	console.log(obj1);
</script>

结果如下图所示:
在这里插入图片描述
此时,不仅指定了原型,而且给当前的对象(obj1)扩展了属性。如果console.log(obj1.sex);则可以返回结果


<script type="text/javascript">
	var obj = {username: 'damu' , age: 30}
	var obj1 = {};
	obj1 = Object.create(obj , {
		sex: {
			value: '男',
//			writable: true,
//			configurable: true
//			enumerable: true
		}
	});
	console.log(obj1);
	console.log(obj1.sex);//结果为:男
	obj1.sex = '女';//此时,修改失败,需要指定writable: true
	console.log(obj1.sex);//结果为:男
	delete obj1.sex;//此时,删除失败,需要指定configurable: true
	console.log(obj1);
	for(var i in obj1){/*枚举obj1中的属性*/
		console.log(i);//结果为:username,age。枚举sex属性失败,需要指定enumerable: true
	}

Object.defineProperties

Object.defineProperties(object, descriptors)    (object为指定的对象,descriptors为扩展的属性)
  * 作用: 为指定对象定义扩展多个属性
  * get: 用来获取当前属性值得回调函数
  * set: 监听(修改)当前属性值得触发的回调函数,并且实参即为修改后的值
  * 存取器属性:setter 用来存值 , getter 用来取值
<script type="text/javascript">
	var obj2 = {firstName: 'kobe' , lastName: 'bryant'};
	Object.defineProperties(obj2 , {
		fullName: {//方法实际上是指定的对象去调用,这里是obj2
			get: function () {
				return this.firstName + ' ' + this.lastName;
			}
		}
	})
	console.log(obj2);
</script>

结果如下图所示:
在这里插入图片描述


<script type="text/javascript">
	var obj2 = {firstName: 'kobe' , lastName: 'bryant'};
	Object.defineProperties(obj2 , {
		fullName: {//方法实际上是指定的对象去调用,这里是obj2
			get: function () {//获取扩展属性的值,获取扩展属性值时get方法自动调用
				return this.firstName + ' ' + this.lastName;
			},
			set: function (data) {//监听(修改)扩展属性,当扩展属性发生变化时会自动调用,自动调用后会将变化的值作为实参注入到set函数;这里的data为'tim duncan'。
				console.log('set()' , data);//结果为:set() tim duncan
				/* 修改不了扩展属性(fullName)的值,但能去修改原来属性(firstName和lastName)的值(原来属性的值已经被修改) */
				var names = data.split(' ');//将'tim duncan'拆分为数组
				/* 方法实际上是指定的对象去调用,这里是obj2,因此this是obj2 */
				/* 从原来的属性(firstName和lastName)中获取相应的值 */
				this.firstName = names[0];//值为:tim;
				this.lastName = names[1];//值为:duncan;
			}
		}
	})
	/* 当没有以下代码时,不会自动调用get/set方法 */
	console.log(obj2.fullName);//获取扩展属性值,自动调用get方法 ;结果为:kobe bryant
	obj2.fullName = 'tim duncan';//监听扩展属性值,自动调用set方法;fullName是扩展属性,无法进行修改。
								//但这一句代码是不可缺少的,因为这一句代码的存在才会调用set函数修改firstName和lastName的值。
								//因此,这一句代码的实际作用并非修改fullName,而是传入参数'tim duncan'。
	console.log(obj2.fullName);//再次获取扩展属性值,再次自动调用get方法;结果为:tim duncan
</script>

对象本身的两个方法

对象本身的两个方法
* get propertyName(){} 用来得到当前属性值的回调函数
* set propertyName(){} 用来监视当前属性值变化的回调函数

原理与上述的Object.defineProperties是相同的。

<script type='text/javascript'>
    var obj = {
        firstName : 'kobe',
        lastName : 'bryant',
        /* fullName是根据已有的值动态计算出来的 */
        get fullName(){
            return this.firstName + ' ' + this.lastName
        },
        set fullName(data){
            var names = data.split(' ');
            this.firstName = names[0];
            this.lastName = names[1];
        }
    };
    console.log(obj.fullName);//结果为:kobe bryant
    obj.fullName = 'curry stephen';
    console.log(obj.fullName);//结果为:curry stephen
</script>

Array(数组)扩展

1. Array.prototype.indexOf(value) : 得到值在数组中的第一个下标
2. Array.prototype.lastIndexOf(value) : 得到值在数组中的最后一个下标
3. Array.prototype.forEach(function(item, index){}) : 遍历数组
4. Array.prototype.map(function(item, index){}) : 遍历数组返回一个新的数组,返回加工之后的值
5. Array.prototype.filter(function(item, index){}) : 遍历过滤出一个新的子数组, 返回条件为true的值
<script type="text/javascript">
  /*
   需求:
   1. 输出第一个6的下标
   2. 输出最后一个6的下标
   3. 输出所有元素的值和下标
   4. 根据arr产生一个新数组,要求每个元素都比原来大10
   5. 根据arr产生一个新数组, 返回的每个元素要大于4
   */
  
    var arr = [1, 4, 6, 2, 5, 6];
    console.log(arr.indexOf(6));//结果为:2
    //Array.prototype.lastIndexOf(value) : 得到值在数组中的最后一个下标
    console.log(arr.lastIndexOf(6));//结果为:5

    //Array.prototype.forEach(function(item, index){}) : 遍历数组
    arr.forEach(function (item, index) {
        console.log(item, index);//结果为:1 0,4 1,6 2,2 3,5 4,6 5
    });

    //Array.prototype.map(function(item, index){}) : 遍历数组返回一个新的数组,返回加工之后的值
    var arr1 = arr.map(function (item, index) {
        return item + 10
    });
    console.log(arr, arr1);//结果为:1,4,6,2,5,6   11,14,16,12,15,16

    //Array.prototype.filter(function(item, index){}) : 遍历过滤出一个新的子数组, 返回条件为true的值
    var arr2 = arr.filter(function (item, index) {
        return item > 4
    });
    console.log(arr, arr2);结果为:1,4,6,2,5,6   6 5 6
</script>

Function(函数)扩展

1. Function.prototype.bind(obj) :
  * 作用: 将函数内的this绑定为obj, 并将函数返回
2. 面试题: 区别bind()与call()和apply()?
  * 都能指定函数中的this
  * call()/apply()是立即调用函数
  * bind()是将函数返回
<script type="text/javascript">
	var obj = {username: 'kobe'};
	function foo() {
		console.log(this);
	}
	foo();//结果为:Window
	foo.call(obj);//结果为:{username: "kobe"}
	foo.apply(obj);//结果为:{username: "kobe"}
	/* 可知,在不传参(foo的参数)的情况下,call和apply的使用方式是一样的 */
</script>

<script type="text/javascript">
	var obj = {username: 'kobe'};
	function foo() {
		console.log(this);
	}
	foo();
	
	/* call 直接从第二个参数开始,依次传入参数 */
	foo.call(obj , 33);//结果为:{username: "kobe"} 33
	foo.apply(obj);//结果为:{username: "kobe"} undefined
	foo.apply(obj , 33);//报错
	
	/* apply 第二参数必须是数组,传入的参数放在数组中 */
	foo.apply(obj , [33]);//结果为:{username: "kobe"} 33
	
	/* bind 绑定完this不会立即调用当前的函数,而是将函数返回 */
	foo.bind(obj);//无输出,本质原因是foo函数未调用
	var bar = foo.bind(obj);
	console.log(bar);//将foo函数输出
	bar();//结果为:{username: "kobe"} undefined
	foo.bind(obj)();//或者不需要用var定义,直接调用;结果为:{username: "kobe"} undefined
	/* bind 传参的方法同call一样 */
	foo.bind(obj , 33)();//结果为:{username: "kobe"} 33
	/* 回调函数:我们自己定义的函数,但是由系统调用 */
	/* bind通常指定回调函数的this,示例如下 */
	setTimeout(function () {
		console.log(this);//结果为:{username: "kobe"} 
	}.bind(obj), 1000)//bind将this由window改为obj
</script>

ES6

常用

let & const 关键字

let

let
1. 作用:
  * 与var类似, 用于声明一个变量
2. 特点:
  * 在块作用域内有效
  * 不能重复声明
  * 不会预处理, 不存在提升
3. 应用:
  * 循环遍历加监听
  * 使用let取代var是趋势
<script type="text/javascript">
	console.log(username);//报错,不存在提升
	let username = 'kobe';//与var类似, 用于声明一个变量
	let username = 'wake';//重复声明
	console.log(username);//报错:重复声明
	
	let btns = document.getElementsByTagName('button');//有三个按钮
	
// for(var i = 0;i<btns.length;i++){
//    btns[i].onclick = function () {
//       alert(i); //每个按钮的结果都是3
//    }
// }

   for(let i = 0;i<btns.length;i++){
      btns[i].onclick = function () {
         alert(i);//let有块作用域,每遍历一次时i都是一个版块的,所以在使用i时都是当前块作用域中自己的数据
      }
   }
</script>

const

1. 作用:
  * 定义一个常量
2. 特点:
  * 不能修改
  * 其它特点同let
3. 应用:
  * 保存不用改变的数据
<script type="text/javascript">
    const sex = '男';
    console.log(sex);
    //sex = '女';//不能修改
    console.log(sex);
</script>

变量的解构赋值

1. 理解:
  * 从对象或数组中提取数据, 并赋值给变量(多个)
    (解构赋值的通俗理解:解析其结构,解析完以后知道内部的数据,然后再进行赋值)
2. 对象的解构赋值
  let {n, a} = {n:'tom', a:12}
3. 数组的解构赋值
  let [a,b] = [1, 'atguigu'];
4. 用途
  * 给多个形参赋值
<script type="text/javascript">
	let obj = {username: 'kobe' , age: 39};
	/* 通常写法 */
//	let username = obj.username;
//	let age = obj.age;
	
	/* 对象的解构赋值 */
	/* 
	 * obj是要解构的目标对象,定义两个变量username、age向obj要数据,
	 *   解构的obj是对象,只能以对象的形式来接受数据,而在对象中可以放置属性,
	 * 	 首先,在等号左边定义的属性username、age是相当于在全局定义的变量,与上述的let方式定义是相同的,
	 * 	    在解构赋值的时候,等号左边必须是等号右边的对象已有的属性,否则返回undefined,
	 * 		 并不需要将等号右边的对象所有属性都写上,想要什么属性就写什么属性即可
	 */
	let {username , age} = obj;//解构赋值
	console.log(username , age);//结果为:kobe 39
	console.log(age);//结果为:39
	
	/* 数组的解构赋值 */
	/*
	 * 等号右边是数组,想要结构的对象是数组,
	 *   因此,等号左边也必须用数组的形式来接受数据
	 *     对象用key值一一对应,数组则用下标一一对应,
	 * 		默认是从下标0开始一一对应,也可以直接使用","取得其他的下标
	 */
	let arr = [1, 3 , 5 , 'abc' , true];
	let [] = arr;
	console.log(a , b , c , d , e);//结果为:1 , 3 , 5 , abc , true
	console.log(a , b);//结果为:1 , 3
	console.log(,, a , b);//结果为:5 , abc
	console.log(,,, a , b);//结果为:abc , true
	
	function foo(obj) {
		console.log(obj.username , obj.age);
	}
	foo(obj);//结果为:kobe 39
	
	/* 给多个形参赋值 */
	/*
	 * 用{username , age}代替obj
	 * 	前提是知道传入的对象obj中有username、age,
	 * 	
	 */
	function foo({username , age}) {//相当于在函数内部声明了username、age,与对象的解构赋值在全局声明一样
		console.log(username , age);//直接使用
	}
	foo(obj);//结果为:kobe 39
</script>

模板字符串

1. 模板字符串 : 简化字符串的拼接
  * 模板字符串必须用 `` 包含
  * 变化的部分使用 ${xxx} 定义
<script type="text/javascript">
    let obj = {
        name : 'anverson',
        age : 41
    };
    /* 普通的拼串效率低且容易拼错 */
    console.log('我叫:' + obj.name + ', 我的年龄是:' + obj.age);
		/* 模板字符串 */
    console.log(`我叫:${obj.name}, 我的年龄是:${obj.age}`);
</script>

对象的简写方式

简化的对象写法
* 省略同名的属性值
* 省略方法的function
* 例如:
	  let x = 1;
	  let y = 2;
	  let point = {
	    x,
	    y,
	    setX (x) {this.x = x}
	  };
<script type="text/javascript">
	let username = 'kobe';
	let age = 39;
	let obj = {
		username: username,
		age: age
		getName: function () {
			return this.username;
		}
	};
	console.log(obj);//结果为:{username: "kobe", age: 39}
	console.log(obj.getName());//结果为:kobe
	
	/* 在ES6中 */
	let obj = {
		username,	/* 同名的属性可以省略不写 */
		age,	/* 同名的属性可以省略不写 */
		getName() { /* 函数的function可以省略不写 */
			return this.username;
		}
	};
	console.log(obj);//结果为:{username: "kobe", age: 39, getName: ƒ}
	console.log(obj.getName());//结果为:kobe
</script>

箭头函数详解

* 作用: 定义匿名函数
* 基本语法:
  * 没有参数: () => console.log('xxxx')
  * 一个参数: i => i+2
  * 大于一个参数: (i,j) => i+j
  * 函数体不用大括号: 默认返回结果
  * 函数体如果有多个语句, 需要用{}包围,若有需要返回的内容,需要手动返回
* 使用场景: 多用来定义回调函数

* 箭头函数的特点:
    1、简洁
    2、箭头函数没有自己的this,箭头函数的this不是调用的时候决定的,而是在定义的时候处在的对象就是它的this
    3、通俗理解: 箭头函数的this看外层的是否有函数,
         如果有,外层函数的this就是内部箭头函数的this,
         如果没有,则this是window。
<body>
    <button id="btn">测试箭头函数this_1</button>
    <button id="btn2">测试箭头函数this_2</button>
    
	<script type="text/javascript">
	  
	//let fun = function () {
	//	console.log('我是箭头函数');
	//}
	  
	  /* 箭头函数 */
	  /* 括号中的是形参,箭头右侧是函数体内容 */
	  let fun = () => console.log('我是箭头函数');
	  fun();//结果为:我是箭头函数
	  
	  /* 形参的情况 */
	  // 1.没有形参的时候,()不能省略
	  let fun1 = () => console.log('我是箭头函数');
	  fun1();//结果为:我是箭头函数
	  
	  // 2.只有一个形参的时候,()可以省略
	  let fun2 = (a) => console.log(a);
	//let fun2 = a => console.log(a);
	  fun2('aaa');
	  fun2();//结果为:aaa
	  
	  // 3.两个及两个以上的形参的时候,()不能省略
	  let fun3 = (x, y) => console.log(x, y);  
	  fun3(25, 36);//结果为:25,36
	  
	  /* 函数体的情况 */
	  // 1.函数体只有一条语句或者是表达式的时候,{}可以省略 ---> 会自动返回语句执行的结果或者表达式的结果
	  let fun4 = () => {console.log('我是箭头函数');}
	  let fun4 = (x, y) => x + y;//省略(),x+y会自动执行
	  console.log(fun4(24, 36));//结果为:60
	  //如果加上{},则需要用return来获取值
	  let fun4 = (x, y) => {return x + y};
	  //如果有{}时不加return,则console.log(fun4(24, 36));的结果是undefined
	  
	  // 2.函数体不止一条语句或表达式时,{}不可以省略
	  let fun5 = (x, y) => {
	  	console.log(x, y);
	  	return x + y;//同样需要return,不过不写return,则console.log(fun5(35, 49));的结果为undefined
	  }
	  console.log(fun5(35, 49));
	  
	  /* 测试箭头函数的this */
	  /* 箭头函数的this看外层的是否有函数(非箭头函数的函数),
	   *   如果有,外层函数的this就是内部箭头函数的this,
	   *   如果没有,则this是window。
	   */
	  let btn1 = document.getElementById('btn1');
	  let btn2 = document.getElementById('btn2');
	  btn1.onclick = function () {
	  	alert(this);//结果为:btn1  谁调用,this就是谁
	  }
	  
	  btn2.onclick = () => {
	  	alert(this);//结果为:window  定义时所处的对象是window,所以this是window
	  }
	  
	  let obj = {
	  	name: '箭头函数',
	  	getName: function () {
	  		btn2.onclick = () => {
	  			console.log(this);
	  		}
	  	}
	  }
	  obj.getName();//结果为:obj
	  
	  let obj = {
	  	name: '箭头函数',
	  	getName: () => { /* 相当于obj.getName = () => {}; */
	  		btn2.onclick = () => {
	  			console.log(this);
	  		}
	  	}
	  }
	  obj.getName();//结果为:window
	
	</script>
</body>

三点运算符

* 用途
1. rest(可变)参数
    * 用来取代arguments 但比arguments灵活,只能是最后部分形参参数
  function add(...values) {
    let sum = 0;
    for(value of values) {
      sum += value;
    }
    return sum;
  }
2. 扩展运算符
  let arr1 = [1,3,5];
  let arr2 = [2,...arr1,6];
  arr2.push(...arr1);
<script type="text/javascript">
	
  function foo (a , b) {
  	console.log(arguments);
//	arguments.callee();//callee表示函数本身,这行代码表示递归
    arguments.forEach(function (item , index) {
      console.log(item , index);//因为是伪数组,因此遍历时会报错
    });
  }
  foo(2,65);
  
  /* -------------分割线------------- */
 
  function foo (...value) {
  	console.log(...value);
//  arguments.forEach(function (item , index) {
//    console.log(item , index);//因为是伪数组,因此遍历时会报错
//  });
		console.log(value);
		arguments.forEach(function (item , index) {
      console.log(item , index);//不会报错;说明三点运算符收集的实参就是一个普通的数组,此时可以进行遍历
    });
  }
  foo(2,65);
  
  /* -------------分割线------------- */
  
  //如果是如下形式的函数,则第一个实参不会被三点运算符收集,第一个实参对应a
  function foo (a , ...value){...}
  //如果是如下形式的函数,三点运算符不是最后部分形参参数,则会报错
  function foo (a , ...value , b){...}

</script>

形参默认值

* 形参的默认值----当不传入参数的时候默认使用形参里的默认值
function Point(x = 1,y = 2) {
this.x = x;
this.y = y;
}
<script type="text/javascript">
    //定义一个点的坐标的构造函数
    function Point(x , y) {
        this.x = x;
        this.y = y;
    }
    let point = new Point(25, 36);
    console.log(point);
    
    let p = new Point();//没有传参数,但不会报错,默认为undefined
    console.log(p);
    
    function Point(x=3 , y=4) {
        this.x = x;
        this.y = y;
    }
    let p = new Point();//此时x、y的默认值分别为3和4
    console.log(p);
</script>

promise对象

原理

1. 理解:
  * Promise对象: 代表了未来某个将要发生的事件(通常是一个异步操作)
  * 有了promise对象, 可以将异步操作以同步的流程表达出来, 避免了层层嵌套的回调函数(俗称'回调地狱')
  * ES6的Promise是一个构造函数, 用来生成promise实例
2. 使用promise基本步骤(2步):
  * 创建promise对象
    let promise = new Promise((resolve, reject) => {
        //初始化promise状态为 pending
      //执行异步操作
      if(异步操作成功) {
        resolve(value);//修改promise的状态为fullfilled
      } else {
        reject(errMsg);//修改promise的状态为rejected
      }
    })
  * 调用promise的then()
    promise.then(function(
      result => console.log(result),
      errorMsg => alert(errorMsg)
    ))
3. promise对象的3个状态
  * pending: 初始化状态
  * fullfilled: 成功状态
  * rejected: 失败状态
4. 应用:
  * 使用promise实现超时处理

  * 使用promise封装处理ajax请求
    let request = new XMLHttpRequest();
    request.onreadystatechange = function () {
    }
    request.responseType = 'json';
    request.open("GET", url);
    request.send();
<script type="text/javascript">

  //创建一个promise实例对象
  let promise = new Promise((resolve, reject) => {
    //初始化promise的状态为 pending(初始化状态)
    console.log('111');//同步执行,先输出111,再输出222
    //执行异步操作,通常是发送ajax请求,开启定时器
    setTimeout(function () {
      console.log('333');
      /* 根据异步任务的返回结果来修改promise的状态 */
      //异步任务执行成功
      resolve('哈哈');//修改promise的状态为 fullfilled(成功状态),然后会自动调用then中的第一个函数,结果为:成功了 哈哈
      //异步任务执行失败
      reject('嘿嘿');//修改promise的状态为 rejected(失败状态),然后会自动调用then中的第二个函数,结果为:失败了 嘿嘿
    },2000)
  });
  /* 以下的data对应 resolve() 中的形参,error对应 reject() 中的形参 */
  promise.then((data) => {//第一个函数
    console.log('成功了 ' + data);//成功的回调
  }, (error) => {//第二个函数
    console.log('失败了 ' + error);//失败的回调
  });
  console.log('222');
  
</script>

在这里插入图片描述


实例案例
对于网页中的新闻来说,一般上方是新闻的内容,下方是新闻的评论。新闻的内容肯定是要发请求给服务器从数据库获取的,那么怎么才能知道该拿的是哪一条新闻呢?在当初设置字段的时候会给每一个新闻都设置一个id,通过id寻找对应的新闻,那么下方的评论内容又怎么办呢,是新闻和评论一起请求还是先请求新闻内容再请求评论内容呢?将新闻内容和评论内容绑定到一起存入数据库,它们合在一起是一个字段,这当然没有问题,但有以下问题:1.不容易管理,数据量更大;2.有时候评论的内容比新闻的内容多得多,这时候,工作量会很大,如果浏览器渲染得慢,用户体验就不好,用户看到内容一点点地显示出来。而先请求新闻内容再请求评论内容的方式,有以下好处:1.数据少,页面渲染得快,这时,将新闻内容展现给用户的时间就会相对更短,用户体验更好,先让用户看到新闻内容,在用户看新闻内容的时候再发一次请求根据新闻内容的id找到相应评论的,再将评论渲染出来

/* 路由 */
router.get('/news', function (req, res, next) {
    res.set('Access-Control-Allow-Origin', '*');
    var id = req.query.id;
    console.log('/news id='+id);
    var news = {//假数据:当前的新闻内容
        id: id,
        title: 'news title1...',
        content: 'news content1...',
        commentsUrl: '/comments?newsId='+id//评论内容
    };
    res.json(news);//返回新闻内容数据
});

router.get('/comments', function (req, res, next) {
    res.set('Access-Control-Allow-Origin', '*');
    console.log('/comments newsId=' + req.query.newsId);
    var newsId = req.query.newsId;
    var comments = [
        {
            id: 1,
            content: 'comment content1111...',
            newsId : newsId
        },
        {
            id: 2,
            content: 'comment content2222...',
            newsId : newsId
        }];
    res.json(comments);
});

module.exports = router;
<script type="text/javascript">
   /* 需求:
    * 1、发送ajax请求获取新闻内容
    * 2、新闻内容获取成功后再次发送请求,获取对应的新闻评论内容
    * 3、新闻内容获取失败则不需要再次发送请求。
    */ 
    
 	//定义获取新闻的功能函数
	function getNews(url) {
    let promise = new Promise((resolve, reject) => {
      //初始化promise状态为pending
      //启动异步任务
      //创建xmlHttp实例对象
      let xmlHttp = new XMLHttpRequest();
      //绑定监听 readyState
      xmlHttp.onreadystatechange = function () {
        if(xmlHttp.readyState === 4){
        	if(xmlHttp.status === 200){//请求成功
						//修改状态
						resolve(xmlHttp.responseText);//修改promise的状态为成功状态
        	}else{//请求失败
          	reject('暂时没有新闻内容');//修改promise的状态为失败状态
        	}
        }
      };
      
	    //open 设置请求的方式以及url
	    xmlHttp.open('GET', url);
	    //发送
	    xmlHttp.send();
    })
	  return promise;
  }
	
  getNews('http://localhost:3000/news?id=2')
	  .then((data) => {//请求成功
			console.log(data);
			//发送请求获取评论内容准备url
			let commentsUrl = JSON.parse(data).commentsUrl;
			let url = 'http://localhost:3000' + commentsUrl;
			//发送请求
			return getNews(url);
	  }, (error) => {//请求失败
	    console.log(error);
	  })
	  .then((data) => {
      console.log(data);
	  }, (error) => {
      console.log(error);
	  }) 
            
</script>

/* 老师的备课笔记 */
//定义一个请求news的方法
function getNews(url) {
    //创建一个promise对象
    let promise = new Promise((resolve, reject) => {
        //初始化promise状态为pending
        //启动异步任务
        let request = new XMLHttpRequest();
        request.onreadystatechange = function () {
            if(request.readyState === 4){
                if(request.status === 200){
                    let news = request.response;
                    resolve(news);
                }else{
                    reject('请求失败了。。。');
                }
            }
        };
        request.responseType = 'json';//设置返回的数据类型
        request.open("GET", url);//规定请求的方法,创建链接
        request.send();//发送
    })
    return promise;
}

getNews('http://localhost:3000/news?id=2')
  .then((news) => {//请求成功
      console.log(news);
      document.write(JSON.stringify(news));
      console.log('http://localhost:3000' + news.commentsUrl);
      return getNews('http://localhost:3000' + news.commentsUrl);
  }, (error) => {//请求失败
      alert(error);
  })
  .then((comments) => {
      console.log(comments);
      document.write('<br><br><br><br><br>' + JSON.stringify(comments));
  }, (error) => {
      alert(error);
  }) 
使用promise基本步骤(2):
  * 创建promise对象
    let promise = new Promise((resolve, reject) => {
        //初始化promise状态为 pending
      //执行异步操作
      if(异步操作成功) {
        resolve(value);//修改promise的状态为fullfilled
      } else {
        reject(errMsg);//修改promise的状态为rejected
      }
    })
  * 调用promise的then()
    promise.then(function(
      result => console.log(result),
      errorMsg => alert(errorMsg)
    ))
3. promise对象的3个状态
  * pending: 初始化状态
  * fullfilled: 成功状态
  * rejected: 失败状态
4. 应用:
  * 使用promise实现超时处理

  * 使用promise封装处理ajax请求
    let request = new XMLHttpRequest();
    request.onreadystatechange = function () {
    }
    request.responseType = 'json';
    request.open("GET", url);
    request.send();

Symbol属性介绍

前言:ES5中对象的属性名都是字符串,容易造成重名,污染环境
Symbol:
   概念:ES6中的添加了一种原始数据类型symbol(已有的原始数据类型:String, Number, boolean, null, undefined, 对象)
   特点:
     1、Symbol属性对应的值是唯一的,解决命名冲突问题
     2、Symbol值不能与其他数据进行计算,包括同字符串拼串
     3、for in, for of遍历时不会遍历symbol属性。
   使用:
     1、调用Symbol函数得到symbol值
       let symbol = Symbol();
       let obj = {};
       obj[symbol] = 'hello';
     2、传参标识
       let symbol = Symbol('one');
       let symbol2 = Symbol('two');
       console.log(symbol);// Symbol('one')
       console.log(symbol2);// Symbol('two')
     3、内置Symbol值
       * 除了定义自己使用的Symbol值以外,ES6还提供了11个内置的Symbol值,指向语言内部使用的方法。
       - Symbol.iterator
        * 对象的Symbol.iterator属性,指向该对象的默认遍历器方法(也可见iterator接口机制)
<script type="text/javascript">
	
	//创建symbol属性值
  let symbol = Symbol();
  console.log(typeof symbol);//结果为:symbol
  console.log(symbol);//结果为:Symbol()
  
  //用作对象的属性(唯一)
  let obj = {username: 'kobe', age: 39};
//    obj.sex = '男';
//    obj.Symbol //错误写法
  obj[symbol] = 'symbol';
  console.log(obj);//结果为:{username: "kobe", age: 39, Symbol(): "symbol"}
  obj[symbol] = 'hello';
  console.log(obj);//结果为:{username: "kobe", age: 39, Symbol(): "hello"}
  
  /* Symbol属性对应的值是唯一的,解决命名冲突问题 */
  let symbol2 = Symbol();
  let symbol3 = Symbol();
  console.log(symbol2 == symbol3);//结果为:false
  console.log(symbol2, symbol3);//结果为:Symbol() Symbol()
  let symbol4 = Symbol('one');
  let symbol5 = Symbol('two');     
  console.log(symbol4 == symbol5);//结果为:false
  console.log(symbol4, symbol5);//结果为:Symbol(one) Symbol(two)
  
  //可以去定义常量
  const Person_key = Symbol('person_key');
  console.log(Person_key);//结果为:Symbol(person_key)
  
  
  //for in, for of 不能遍历symbol属性。
  console.log(obj);//结果为:{username: "kobe", age: 39, Symbol(): "hello"}
  for(let i in obj){
    console.log(i);//结果为:username age
  }
   
</script>

/* 对象的Symbol.iterator属性,指向该对象的默认遍历器方法(后边讲)(见iterator接口机制) */
//使用三点运算符,解构赋值,默认去调用iterator接口
let arr2 = [1, 6];
let arr3 = [2, 3, 4, 5];
arr2 = [1, ...arr3, 6];
console.log(arr2);//结果为:[1, 2, 3, 4, 5, 6]
/*
 * 为什么这样可以插入呢?
 * 	 实际上就是调用iterator接口,去遍历拿到arr3中的每一个值
 */

let [a, b] = arr2;
console.log(a, b);//结果为:1 2
/*
 * 同理,也是调用iterator接口进行遍历
 */

iterator接口机制

概念: iterator是一种接口机制,为各种不同的数据结构提供统一的访问机制
作用:
  1、为各种数据结构,提供一个统一的、简便的访问接口;
  2、使得数据结构的成员能够按某种次序排列
  3、ES6创造了一种新的遍历命令for...of循环,Iterator接口主要供for...of消费。
工作原理:
  - 创建一个指针对象(遍历器对象),指向数据结构的起始位置。
  - 第一次调用next方法,指针自动指向数据结构的第一个成员
  - 接下来不断调用next方法,指针会一直往后移动,直到指向最后一个成员
  - 每调用next方法返回的是一个包含value和done的对象,{value: 当前成员的值,done: 布尔值}
    * value表示当前成员的值,done对应的布尔值表示当前的数据的结构是否遍历结束。
    * 当遍历结束的时候返回的value值是undefined,done值为false
原生具备iterator接口的数据(可用for of遍历)
  1、Array
  2、arguments
  3、set容器
  4、map容器
  5、String
<script type="text/javascript">	
/* 手动模拟ES6中的iterator是如何实现的 */
	//模拟指针对象(遍历器对象)
	function myIterator (arr) {//iterator接口
		let nextIndex = 0;//记录指针的位置;
		return {//遍历器对象
			next: function () {
				return nextIndex < arr.length ? {value: arr[nextIndex++], done: false} : {value: undefined, done: true};
			}
		}
	}

	//准备一个数据
	let arr = [1, 4, 65, 'abc'];
 	let iteratorObj = myIterator(arr);
 	console.log(iteratorObj.next());//结果为:{value: 1, done: false}
 	console.log(iteratorObj.next());//结果为:{value: 4, done: false}
 	console.log(iteratorObj.next());//结果为:{value: 65, done: false}
 	console.log(iteratorObj.next());//结果为:{value: "abc", done: false}
 	console.log(iteratorObj.next());//结果为:{value: undefined, done: true}
            
	//将iterator接口部署到指定的数据类型上,可以使用for of去循环遍历
	//数组、字符串、arguments、set容器、map容器
	  
	for(let i of arr){//遍历数组
	  console.log(i);//结果为:1 4 65 abc
	}
	  
	let str = 'abcdefg';
	for(let i of str){//遍历字符串
	  console.log(i);//结果为:a b c d e f g
	}
	  
	function fun() {//遍历arguments
	  for(let i of arguments){
	  	console.log(i);
	  }
	}
	fun(1, 4, 5, 'abc');//结果为:1 4 5 abc
	  
	let obj = {username: 'kobe', age: 39}
	for(let i of obj){//遍历普通对象
	   console.log(i);//报错:Uncaught TypeError: obj is not iterable
	}     
	  
	//等同于在指定的数据结构上部署了iterator接口
	//当使用for of去遍历某一个数据结构的时候,首先去找Symbol.iterator,找到了就去遍历,没有找到的话则不能遍历: xxx is not iterable
	let targetData = {
		[Symbol.iterator]: function () {
			let nextIndex = 0;//记录指针的位置;
			return {//遍历器对象
				next: function () {//下面的this指的是targetData
					return nextIndex < this.length ? {value: arr[nextIndex++], done: false} : {value: undefined, done: true};
				}
			}
		}
	}
 	    
</script>

Generator函数

函数简介

概念:
  1、ES6提供的解决异步编程的方案之一
  2、Generator函数是一个状态机,内部封装了不同状态的数据,
  3、用来生成遍历器对象
  4、可暂停函数(惰性求值), yield可暂停,next方法可启动。每次返回的是yield后的表达式结果
特点:
  1、function 与 函数名 之间有一个星号(*)
  2、内部用yield表达式来定义不同的状态
  例如:
    function* generatorExample(){
      let result = yield 'hello';  // 状态值为hello
      yield 'generator'; // 状态值为generator
    }
  3、generator函数返回的是指针对象(接11章节里iterator),而不会执行函数内部逻辑
  4、调用next方法函数内部逻辑开始执行,遇到yield表达式停止,返回{value: yield后的表达式结果/undefined, done: false/true}
  5、再次调用next方法会从上一次停止时的yield处开始,直到最后
  6、yield语句返回结果通常为undefined, 当调用next方法时传参内容会作为启动时yield语句的返回值。
<script type="text/javascript">
	
//  function* myGenerator() {
//  	console.log('aaa');
//  	yield 'hello';
//  	yield 'generator';
//  }
//  myGenerator();//可以发现:console.log('aaa'); 并没有执行。因为yield会暂停,返回的是一个指针对象
		
	/* yield可暂停,next方法可启动。每次返回的是yield后的表达式结果 */
	function* myGenerator() {
	   	console.log('aaa');
	   	yield 'hello';
	   	console.log('bbb');
	   	yield 'generator';
	   	console.log('ccc');
	   	return 'ddd'//用return来指定返回的值
    }
    let MG = myGenerator();//返回的是指针对象
    console.log(MG);//结果为:myGenerator {<suspended>}
    MG.next();//结果为:aaa
    console.log(MG.next());//结果为:bbb  {value: "generator", done: false}
  	console.log(MG.next());//结果为:ccc  {value: "ddd", done: true}
		
	function* aaaa() {
		let result = yield 'fff';
   	    console.log(result);//结果为:undefined(默认值,yield默认返回的是undefined)
	}
	let AA = aaaa();
	console.log(AA.next());//结果为:{value: "fff", done: false}
	/* 如果没有在next()中传入参数,则使当前暂停的yield的返回值默认为undefined */
//	console.log(AA.next());//结果为:undefined  {value: undefined, done: true}
	/* 可在next()中传入参数作为使当前暂停的yield的返回值 */
	console.log(AA.next('ggggg'));//结果为:ggggg  {value: undefined, done: true}

//  let obj = {username: 'kobe', age: 39}
//  for(let i of obj){
//  	console.log(i);//结果为:Uncaught TypeError: obj is not iterable
//  }
		
	//对象的Symbol.iterator属性   指向遍历器对象
	/* 
	 * 人为地部署一个iterator接口,当使用for of遍历时,
	 * 	  默认也是用遍历器对象去调用next()方法进行遍历Generator函数
	 */
	let obj = {username: 'kobe', age: 39}
    obj[Symbol.iterator] = function* myText() {
    	yield 1;
    	yield 2;
    	yield 3;
    }
    for(let i of obj){
    	console.log(i);//结果为:1 2 3
    }
</script>

函数应用

/* 引入jquery */
<script type="text/javascript" src="./js/jquery-1.10.1.min.js"></script>

<script type="text/javascript">

	 /*
    * 需求:
    * 1、发送ajax请求获取新闻内容
    * 2、新闻内容获取成功后再次发送请求,获取对应的新闻评论内容
    * 3、新闻内容获取失败则不需要再次发送请求。
    * */ 
    function* sendXml() {
      //url为next传参进来的数据
      let url = yield getNews('http://localhost:3000/news?newsId=2');//发请求拿到新闻news
      yield getNews(url);//发请求拿到评论内容comments
    }
    function getNews(url) {
      $.get(url, function (data) {
        console.log(data);
        let commentsUrl = data.commentsUrl;
        let url = 'http://localhost:3000' + commentsUrl;
        //当获取新闻内容成功,发送请求获取对应的评论内容
        //调用next传参会作为上次暂停时yield的返回值
        sx.next(url);
      })
    }
    
		//获取遍历器对象
    let sx = sendXml();
    //发送请求获取新闻内容
    sx.next();

</script>

async函数

详解及应用

async函数(源自ES2017)
概念: 真正意义上去解决异步回调的问题,同步流程表达异步操作
本质: Generator的语法糖
语法:
    async function foo(){
      await 异步操作;
      await 异步操作;
    }
特点:
  1、不需要像Generator去调用next方法,遇到await等待,当前的异步操作完成就往下执行
  2、返回的总是Promise对象,可以用then方法进行下一步操作
  3、async取代Generator函数的星号*,await取代Generator的yield
  4、语意上更为明确,使用简单,经临床验证,暂时没有任何副作用
<script type="text/javascript" src="./js/jquery-1.10.1.min.js"></script>

<script type="text/javascript">
	
	//async基本使用
	async function foo() {
	    return new Promise(resolve => {
	//  	setTimeout(function () {
	//  		resolve();
	//  	},2000)
	      setTimeout(resolve, 2000);
	    })
	}
	
	async function test() {
		/* 不需要使用类似next()的方法,可以直接开始执行 */
	    console.log('开始执行', new Date().toTimeString());
	    await foo();//结果为等待2s
	    console.log('执行完毕', new Date().toTimeString());//等待2s后执行
	}
	test();

  
    //await的返回值 
    function test2() {
      return 'aaa';
    }
    async function asyncPrint() {
//	  /* 如果是普通的函数,返回值就是await之后函数执行完的返回值 */
//    let result = await test2();//结果为:aaa

//	  let result = await Promise.resolve();//可以将promise大写,直接作为对象使用
//    console.log(result);//结果为:undefined(默认值)
    
      let result = await Promise.resolve('bbb');
      console.log(result);//结果为:bbb
    
      result = await Promise.reject('ccccc');
      console.log(result);//报错:Uncaught (in promise) ccccc
    }
    asyncPrint();

	// 案例演示
	/* 需求:
     * 1、发送ajax请求获取新闻内容
     * 2、新闻内容获取成功后再次发送请求,获取对应的新闻评论内容
     * 3、新闻内容获取失败则不需要再次发送请求。
     */
	async function getNews(url) {
      return new Promise((resolve, reject) => {
        $.ajax({
      	  method: 'GET',
          url,//ES6中同名的属性可以省略不写
//	          success: function(data) {
//	          	resolve();
//	          }
          success: data => resolve(data),// 用=>函数表示
//	          error: function(error) {
//	        	  reject();
//	          }
          error: error => reject(error)// 用=>函数表示
        })
      })
	}
	
	async function sendXml(url) {
	  let result = await getNews('http://localhost:3000/news?id=7');
	  console.log(result);
	  result = await getNews('http://localhost:3000' + result.commentsUrl);
      console.log(result);
	}
	sendXml();

</script>

应用技巧

上述代码中,如果请求失败了,用户是看不到控制台的反馈的,怎么修改才能使得,用户知道请求失败了呢?
/* 只列出修改部分的代码 */
success: data => resolve(data),
//将error也用resolve表示,然后可以将参数改为例如false
error: error => resolve(false)//此时,返回的是false

async function sendXml(url) {
   let result = await getNews('http://localhost:3000000000/news?id=7');//改为错误的参数,此时result的结果为false
   console.log(result);
   if(!result){//进行判断
     alert('暂时没有新闻可以推送');
     return;
   }
   result = await getNews('http://localhost:3000' + result.commentsUrl);
   console.log(result);
}

class类使用详解

1. 通过class定义类/实现类的继承
2. 在类中通过constructor定义构造方法
3. 通过new来创建类的实例
4. 通过extends来实现类的继承
5. 通过super调用父类的构造方法
6. 重写从父类中继承的一般方法

先来看以前的方法

<script type="text/javascript">
   function Person(name , age) {
      this.name = name;
      this.age = age;
   }
   let person = new Person('kobe', 39);
   console.log(person);
</script>

结果如下图所示:
在这里插入图片描述
可以发现,在_proto_中有constructor因此在类class中定义的所有方法都会放在原型上这样做的好处是只在内存中占一个位置,每生成一个实例,都可以去用这个方法。


<script type="text/javascript">

	//定义一个人物的类
	class Person {
		//类的构造方法
		constructor(name, age) {
			this.name = name;
			this.age = age;
		}
		//类的一般方法
		//在class中必须用对象的简写方式,省略函数的function
		showName() {
			console.log(this.name);//结果为:kobe
		}
		
	}
	let person = new Person('kobe', 39);
	console.log(person);
	person.showName();
    
</script>

结果如下图所示:
在这里插入图片描述


<script type="text/javascript">

	//定义一个人物的类
	class Person {
		//类的构造方法
		constructor(name, age) {
			this.name = name;
			this.age = age;
		}
		//类的一般方法
		//在class中必须用对象的简写方式,省略函数的function
		showName() {
			console.log('这是父类的showName');			
			console.log(this.name);
		}
		
	}
	let person = new Person('kobe', 39);
	console.log(person);
	person.showName();//结果为:kobe
	
	//子类
	class StarPerson extends Person {
		constructor(name, age, salary) {
			super(name, age);//调用父类的构造方法  相当于父类中的 this.name = name;  this.age = age;
			this.salary = salary;
		}
		//父类的方法重写
		showName(){
			console.log('这是子类的showName');
			console.log(this.name, this.age, this.salary);
		}
	}
	let p1 = new StarPerson('wade', 36, 100000000);
	console.log(p1);
	p1.showName();//结果为:这是子类的showName  wade 36 100000000
    
</script>

结果如下图所示:
在这里插入图片描述

其他

字符串、数值的扩展

字符串

1. includes(str) : 判断是否包含指定的字符串
2. startsWith(str) : 判断是否以指定字符串开头
3. endsWith(str) : 判断是否以指定字符串结尾
4. repeat(count) : 重复指定次数
<script type="text/javascript">

    let str = 'abcdefg';
    
    /* includes(str) : 判断是否包含指定的字符串 */
    console.log(str.includes('a'));//结果为:true
    console.log(str.includes('h'));//结果为:false
    
	/* startsWith(str) : 判断是否以指定字符串开头 */
    console.log(str.startsWith('a'));//结果为:true
    console.log(str.startsWith('d'));//结果为:false
    
	/* endsWith(str) : 判断是否以指定字符串结尾 */
    console.log(str.endsWith('g'));//结果为:true
    console.log(str.endsWith('d'));//结果为:false
    
	/* repeat(count) : 重复指定次数 */
    console.log(str.repeat(5));//结果为:abcdefgabcdefgabcdefgabcdefgabcdefg
    
</script>

数值

1. 二进制与八进制数值表示法: 二进制用0b, 八进制用0o
2. Number.isFinite(i) : 判断是否是有限大的数
3. Number.isNaN(i) : 判断是否是NaN
4. Number.isInteger(i) : 判断是否是整数
5. Number.parseInt(str) : 将字符串转换为对应的数值
6. Math.trunc(i) : 直接去除小数部分
<script type="text/javascript">
	
    console.log(0b1010);//结果为:10
    console.log(0o56);//结果为:46
    
    /* Number.isFinite(i) : 判断是否是有限大的数 */
    console.log(Number.isFinite(NaN));//结果为:false
    console.log(Number.isFinite(5));//结果为:true
    
    /* Number.isNaN(i) : 判断是否是NaN */
    console.log(Number.isNaN(NaN));//结果为:true
    console.log(Number.isNaN(5));//结果为:falsse

    /* Number.isInteger(i) : 判断是否是整数 */
    console.log(Number.isInteger(5.23));//结果为:false
    console.log(Number.isInteger(5.0));//结果为:true
    console.log(Number.isInteger(5));//结果为:true

    /* Number.parseInt(str) : 将字符串转换为对应的数值 */
    console.log(Number.parseInt('123abc'));//结果为:123
    console.log(Number.parseInt('a123abc'));//结果为:NaN

    /* Math.trunc(i) : 直接去除小数部分 */
    console.log(Math.trunc(13.123));//结果为:13
    console.log(Math.trunc(13.789));//结果为:13

</script>

数组方法的扩展

1. Array.from(v) : 将伪数组对象或可遍历对象转换为真数组
2. Array.of(v1, v2, v3) : 将一系列值转换成数组
3. find(function(value, index, arr){return true}) : 找出第一个满足条件返回true的元素
4. findIndex(function(value, index, arr){return true}) : 找出第一个满足条件返回true的元素下标
<body>
	<button>测试1</button>
	<button>测试2</button>
	<button>测试3</button>
	
	<script type="text/javascript">
	  
	    /* Array.from(v) : 将伪数组对象或可遍历对象转换为真数组 */
	    let btns = document.getElementsByTagName('button');
	//  btns.forEach(function (item, index) {
	//	  console.log(item);//报错,因为这是伪数组,没有数组的一般方法
	//  })
		Array.from(btns).forEach(function (item, index) {
			console.log(item);//结果为:<button>测试1</button>  <button>测试2</button>  <button>测试3</button>  
		})
		
		/* Array.of(v1, v2, v3) : 将一系列值转换成数组 */
		let arr = Array.of(1, 4, 'abc', true);
		console.log(arr);//结果为:(4) [1, 4, "abc", true]
		
		/* find(function(value, index, arr){return true}) : 找出第一个满足条件返回true的元素 */
		let arr2 = [2, 3, 4, 2, 5, 7, 3, 6, 5];
		let result = arr2.find(function (item, index) {
			return item > 4
		});
		console.log(result);//结果为:5
		
		/* findIndex(function(value, index, arr){return true}) : 找出第一个满足条件返回true的元素下标 */
		let arr3 = [1, 2, 3, 4, 5, 6, 7];
		let result2 = arr3.findIndex(function (item, index) {
			return item > 4
		});
		console.log(result2);//结果为:4
	  
	</script>
</body>

对象方法的扩展

1. Object.is(v1, v2)
  * 判断2个数据是否完全相等
2. Object.assign(target, source1, source2..)
  * 将源对象的属性复制到目标对象上
3. 直接操作 __proto__ 属性
  let obj2 = {};
  obj2.__proto__ = obj1;
<script type="text/javascript">
	
	/* Object.is(v1, v2) */
	console.log(0 == -0);//结果为:true
	console.log(NaN == NaN);//结果为:false
	//Object.is(v1, v2)底层以字符串的形式来判断
	console.log(Object.is(0, -0));//结果为:false
	console.log(Object.is(NaN, NaN));//结果为:true
    
    /* Object.assign(target, source1, source2..) */
    let obj = {};
    let obj1 = {username: 'anverson', age: 42};
    let obj2 = {sex: '男'};
//	  Object.assign(obj, obj1);
//	  console.log(obj);//结果为:{username: "anverson", age: 42}
    Object.assign(obj, obj1, obj2);
    console.log(obj);//结果为:{username: "anverson", age: 42, sex: "男"}
  
    /* 直接操作 __proto__ 属性 */
    let obj3 = {};
    let obj4 = {qian: 50000000};
    obj3.__proto__ = obj4;
    console.log(obj3.qian);//结果为:50000000  注意:.qian在__proto__中
    console.log(obj3);//结果为:如图所示
  
</script>

结果如下图所示:
在这里插入图片描述

深度克隆

1、数据类型:
  * 数据分为基本的数据类型(String, Number, boolean, Null, Undefined)和对象数据类型
  - 基本数据类型:
    特点: 存储的是该对象的实际数据
  - 对象数据类型:
    特点: 存储的是该对象在栈中引用,真实的数据存放在堆内存里
2、复制数据
  - 基本数据类型存放的就是实际的数据,可直接复制
    let number2 = 2;
    let number1 = number2;
  - 克隆数据:对象/数组
    1、区别: 浅拷贝/深度拷贝
       判断: 拷贝是否产生了新的数据还是拷贝的是数据的引用
       知识点:对象数据存放的是对象在栈内存的引用,直接复制的是对象的引用
       let obj = {username: 'kobe'}
       let obj1 = obj; // obj1 复制了obj在栈内存的引用
    2、常用的拷贝技术
      1). arr.concat(): 数组浅拷贝
      2). arr.slice(): 数组浅拷贝
      3). JSON.parse(JSON.stringify(arr/obj)): 数组或对象深拷贝, 但不能处理函数数据
      4). 浅拷贝包含函数数据的对象/数组
      5). 深拷贝包含函数数据的对象/数组
<script type="text/javascript">
  
    /* 堆内存与栈内存之间的操作 */
    //不会影响原数据
    let str = 'abcd';
    let str2 = str;
    console.log(str2);//结果为:abcd
    str2 = '';
    console.log(str);//结果为:abcd
    let bool1 = true;
    let bool2 = bool1;
    bool2 = false;
    console.log(bool1);//结果为:true
  
    //会影响原始数据
    let obj = {username: 'kobe', age: 39};
    let obj1 = obj;
    console.log(obj1);//结果为:{username: "kobe", age: 39}
    obj1.username = 'wade';
    console.log(obj.username);//结果为:wade
	
	//拷贝数组/对象 没有生成新的数据而是复制了一份引用
	let arr = [1, 4, {username: 'kobe', age: 39}];
	let arr2 = arr;
	arr2[0] = 'abcd';
	console.log(arr, arr2);//结果为:(3) ["abcd", 4, {…}]     (3) ["abcd", 4, {…}]

</script> 
<script type="text/javascript">
  
	/*
	 * 拷贝数据:
	 * 	 基本数据类型:
	 * 	    拷贝后会生成一份新的数据,修改拷贝以后的数据不会影响原数据
	 * 	 对象/数组:
	 * 		拷贝后不会生成新的数据,而是进行引用。修改拷贝以后的数据会影响原数据
	 * 
	 * 拷贝数据的方法:
	 * 	 1. 直接复制给一个变量  //浅拷贝,会影响原数据
	 *   2. Object.assign()  //浅拷贝
	 *   3. Array.prototype.concat()  //浅拷贝
	 * 	 4. Array.prototypr.slice()  //浅拷贝
	 *   5. JSON.parse(JSON.stringify())  //深拷贝(深度克隆) 注意:拷贝的数据不能有函数
	 * 
	 * 浅拷贝(对象/数组):
	 * 	 特点:拷贝的引用,修改拷贝后的数据会影响原数据,使得原数据不安全
	 * 		    因为对象数据存放的是对象在栈内存的引用,直接复制的是对象的引用,
	 *            新的引用会对原来的堆内存中的数据产生影响
	 * 深拷贝(深度克隆)
	 * 	 特点:拷贝的时候生成新的数据,修改拷贝后的数据不会影响原数据
	 * 		    如果遍历的时候拿到的某一项值不是基本数据类型,则继续对其遍历,
	 * 			  直到这一项值中的全部基本类型
	 */
	let obj = {username: 'kobe'};
	
	/* Object.assign() */
	let obj2 = Object.assign(obj);
	console.log(obj2);//结果为:{username: "kobe"}
	obj.username = 'wade';
	console.log(obj);//结果为:{username: "wade"}
	
	/* Array.prototype.concat() */
	let arr = [1, 3, {username: 'kobe'}];
	let testArr = [2, 4];
//	let arr2 = arr.concat(testArr);
//	console.log(arr2);//结果为:(5) [1, 3, {…}, 2, 4]
	let arr2 = arr.concat();
	console.log(arr2);//结果为:(3) [1, 3, {…}]
	arr2[1] = 'a';
	console.log(arr);//结果为:(3) [1, 3, {…}]   
    //可以发现,不影响原数据,没有影响的本质原因是 相当于遍历后拿到每一项的值
    //相当于 : arr2[0] = 1;  这是基本数据类型,所以才不会影响
  
    //arr[2] = {username: 'kobe'} 是可确定的
    //但是,如果是对象
    arr2[2].username = 'wade';
    console.log(arr);//username变为'wade'  说明影响原数据

	/* Array.prototypr.slice() */
	let arr3 = arr.slice();
	arr3[2].username = 'anverson';
	console.log(arr);//username变为'anverson'  说明影响原数据
	
	/* JSON.parse(JSON.stringify()) */
	let arr4 = JSON.parse(JSON.stringify(arr));//将arr变为JSON字符串后再转换为原生的数组
	console.log(arr4);//username为'anverson'  说明没有影响原数据
	arr4[2].username = 'duncan';
	console.log(arr4);//username为'duncan'
	/* JSON字符串 --> JS数组 ,数据类型发生变化,所以不会影响原数据 */
	
	/*
	 * 思考:
	 * 		 如何实现深度拷贝(克隆)
	 * 		 拷贝的数据里有对象/数组、
	 * 		 拷贝的数据里不能有对象/数组,即使有对象/数组,可以继续遍历对象、数组拿到里边每一项值,
	 * 			 一直到拿到的都是基本数据类型,然后再去复制,这就是深度拷贝(克隆)
	 */
	
	//知识点储备
	/*
	 * 如何判断数据类型: arr --> Array   null --> Null
	 * 1. typeof返回的数据类型:String, Number, Boolean, Undefined. Object, Function
	 * 2. Object.prototype.toString.call(obj)  (取到最原始的toString,通过call去指定目标数据,从而检测出目标数据的数据类型)
	 */
	
	let result = 'abcd';
	console.log(Object.prototype.toString.call(result));//结果为:[object String]
	result = null;
	console.log(Object.prototype.toString.call(result));//结果为:[object Null]
	result = [1, 3];
	console.log(Object.prototype.toString.call(result));//结果为:[object Array]
	console.log(typeof Object.prototype.toString.call(result));//结果为:string
	console.log(Object.prototype.toString.call(result).slice(8, -1));//结果为:Array
	
	//for in 循环
	let obj = {username:'kobe', age: 39};//对象
	for(let i in obj) {//枚举对象
		/* 枚举对象时拿到的是属性名 */
		console.log(i);//结果为:username  age  (属性名)
	}
	
	let arr = [1, 3, 'abc'];//数组
	for(let i in arr) {//枚举数组
		/* 枚举数组时拿到的是下标 */
		console.log(i);//结果为:0 1 2  (下标)
	}
	
	//定义检测数据类型的功能函数
	function checkedType(target) {
		return Object.prototype.toString.call(target).slice(8, -1);
	}
//	console.log(checkedType(result));//结果为:Array

	//自己实现深度克隆 --> 对象/数组
	function clone(target) {
		//判断拷贝的数据类型\
		//初始化变量result 成为最终克隆的数据
		let result, targetType = checkedType(target);
		if(targetType === 'Object') {
			result = {};
		}else if(targetType === 'Array'){
			result = [];
		}else{
			return target;
		}
		
		//遍历目标数据
		for(let i in target){
			//获取遍历数据结构的每一项值
			let value = target[i];
			//判断目标结构里的每一项值是否存在对象/数组,如果是对象/数组,则需要继续遍历
			if(checkedType(value) === 'Object' || checkedType(value) === 'Array'){//对象/数组里嵌套了对象/数组
				//继续遍历获取到的value值
				result[i] = clone(value);
			}else{//获取到的value值是基本的数据类型或者是函数
				result[i] = value;
			}
		}
		return result;
	}
	
	let arr3 = [1, 2, {username: 'kobe', age: 39}];
	let arr4 = clone(arr3);
	console.log(arr4);//结果为:1 2 {username: "kobe", age: 39, userName: "wade"}
	arr4[2].username = 'wade';
	console.log(arr3, arr4);//结果为:arr3的username为'kobe'  arr4的username为'wade'
	
	let obj3 = {username: 'kobe', age: 39};
	let obj4 = clone(obj3);
	console.log(obj4);//结果为:{username: "kobe", age: 39}
	obj4.username = 'wade';
	console.log(obj3, obj4);//结果为:{username: "kobe", age: 39}  {username: "wade", age: 39}
	
</script> 

set , map 容器详解

1. Set容器 : 无序不可重复的多个value的集合体
  * Set()
  * Set(array)
  * add(value)
  * delete(value)
  * has(value)
  * clear()
  * size
2. Map容器 : 无序的 key不重复的多个key-value的集合体
  * Map()
  * Map(array)
  * set(key, value)//添加
  * get(key)
  * delete(key)
  * has(key)
  * clear()
  * size
<script type="text/javascript">

    let set = new Set([1,2,4,5,2,3,6]);
    console.log(set);//结果为Set(7) {1 2 4 5 3 6}  length为6  因为2重复了,只留下一个
    set.add(7);
    console.log(set.size, set);//结果为Set(7) {1 2 4 5 3 6 7}
    console.log(set.has(8));//结果为:false
    console.log(set.has(8));//结果为:true
    set.clear();
    console.log(set);//结果为:Set(0) {}
    
    let map = new Map([['aaa', 'username'], [36, 'age']]);
    console.log(map);//结果为:Map(2) {"aaa" => "username", 36 => "age"}
    map.set(78, 'haha');
    console.log(map);//结果为:Map(3) {"aaa" => "username", 36 => "age", 78 => "haha"}
    map.delete(36);
    console.log(map);//结果为:Map(2) {"aaa" => "username", 78 => "haha"}
    
</script>

for of 用法详解

for(let value of target){}循环遍历
  1. 遍历数组
  2. 遍历Set
  3. 遍历Map
  4. 遍历字符串
  5. 遍历伪数组
<script type="text/javascript">
		
	/* 利用set容器去除重复 */
    let arr = [1,2,4,5,5,6,2];
    let arr1 = arr;
    arr = [];
    let set = new Set(arr1);
    for(let i of set) {
    	arr.push(i);
    }
    console.log(arr);//结果为:(5) [1, 2, 4, 5, 6]
    
</script>

ES7

1. 指数运算符(幂): **
2. Array.prototype.includes(value) : 判断数组中是否包含指定value
<script type="text/javascript">
    console.log(3**3);//结果为:27
    
    let arr = [1, 2, 3, 4, 'abc'];
    console.log(arr.includes(2));//结果为:true
    console.log(arr.includes(5));//结果为:false
</script>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值