已更新,见:https://blog.csdn.net/eartholtainanwan/article/details/107343858
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
1. 理解:
* 除了正常运行模式(混杂模式),ES5添加了第二种运行模式:"严格模式"(strict mode)。
* 顾名思义,这种模式使得Javascript在更严格的语法条件下运行
2. 目的/作用
* 消除Javascript语法的一些不合理、不严谨之处,减少一些怪异行为
* 消除代码运行的一些不安全之处,为代码的安全运行保驾护航
* 为未来新版本的Javascript做好铺垫
3. 使用
* 在全局或函数的第一条语句定义为: 'use strict';
* 如果浏览器不支持, 只解析为一条简单的语句, 没有任何副作用
4. 语法和行为改变
* 必须用var声明变量
* 禁止自定义的函数中的this指向window
* 创建eval作用域
* 对象不能有重名的属性
* 函数不能有重名的形参
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>
ES5给Object扩展了好一些静态方法, 常用的2个:
Object.create 和 Object.defineProperties
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, 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>
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>
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>
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[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>
原理
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('
' + 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();
前言: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是一种接口机制,为各种不同的数据结构提供统一的访问机制
作用:
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>
函数简介
概念:
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 {}
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函数(源自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);
}
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);//结果为:
})
/* 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>
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(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>
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>