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 (变化不大)
1. 理解:
* 除了正常运行模式(混杂模式),ES5添加了第二种运行模式:"严格模式"(strict mode)。
* 顾名思义,这种模式使得Javascript在更严格的语法条件下运行
2. 目的/作用
* 消除Javascript语法的一些不合理、不严谨之处,减少一些怪异行为
* 消除代码运行的一些不安全之处,为代码的安全运行保驾护航
* 为未来新版本的Javascript做好铺垫
3. 使用
* 在全局或函数的第一条语句定义为: 'use strict';
* 如果浏览器不支持, 只解析为一条简单的语句, 没有任何副作用
4. 语法和行为改变
* 必须用var声明变量
* 禁止自定义的函数中的this指向window
* 创建eval作用域
* 对象不能有重名的属性
示例代码:
<script type="text/javascript">
'use strict';
// username = 'kobe';
var username = 'kobe';
console.log(username);
function Person (name, age) {
this.name = name;
this.age = age;
}
// Person('kobe', 41);
new Person('kobe', 41);
var str = 'NBA';
eval('var str = "CBA"; alert(str)');
alert(str);
var obj = {
username: 'kobe',
// username: 'wade'
}
</script>
1. JSON.stringify(obj/arr)
* js对象(数组)转换为json对象(数组)
2. JSON.parse(json)
* json对象(数组)转换为js对象(数组)
ES5给Object扩展了一些静态方法, 常用的2个:
1. Object.create(prototype, [descriptors])
* 作用: 以指定对象为原型创建新的对象
* 为新的对象指定新的属性, 并对属性进行描述
- value : 指定值
- writable : 标识当前属性值是否是可修改的, 默认为false
- configurable: 标识当前属性是否可以被删除 默认为false
- enumerable: 标识当前属性是否能用for in 枚举 默认为false
2. Object.defineProperties(object, descriptors)
* 作用: 为指定对象定义扩展多个属性
* get :用来获取当前属性值的回调函数(需要取值时才会调用,“惰性求值”)
* set :修改当前属性值得触发的回调函数,并且实参即为修改后的值
* 存取器属性:setter,getter一个用来存值,一个用来取值
示例代码:
var obj = {username: 'damu', age: 30};
var obj1 = {};
obj1 = Object.create(obj, {
sex: {
value: '男',
writable: true,
configurable: true,
enumerable: true
}
});
console.log(obj1.sex);
obj1.sex = '女';
console.log(obj1.sex);
// delete obj1.sex;
console.log(obj1);
for(var i in obj1){
console.log(i);
}
var obj2 = {firstName: 'kobe', lastName: 'bryant'};
Object.defineProperties(obj2, {
fullName: {
get: function(){// 获取扩展属性的值(获取扩展属性值时get会自动调用)
return this.firstName + ' ' + this.lastName;
},
set: function(data){// 监听扩展属性,当扩展属性发生变化的时候会自动调用(变化后的值作为实参传入)
var names = data.split(' ');
this.firstName = names[0];
this.lastName = names[1];
}
}
})
console.log(obj2.fullName);
obj2.fullName = 'tim duncan';
console.log(obj2.fullName);
console.log(obj2.lastName);
对象本身的两个方法
* get propertyName(){} 用来得到当前属性值的回调函数
* set propertyName(){} 用来监视当前属性值变化的回调函数
示例代码:
var obj = {
firstName: 'curry',
lastName: 'stephen',
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);
obj.fullName = 'kobe bryant';
console.log(obj.fullName);
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的值
示例代码:
/*
需求:
1. 输出第一个6的下标
2. 输出最后一个6的下标
3. 输出所有元素的值和下标
4. 根据arr产生一个新数组,要求每个元素都比原来大10
5. 根据arr产生一个新数组, 返回的每个元素要大于4
*/
var arr = [2,4,3,1,2,6,5,4];
console.log(arr.indexOf(4));
console.log(arr.lastIndexOf(4));
arr.forEach(function(item, index){
console.log(item, index);
})
var arr1 = arr.map(function (item, index) {
return item + 10;
})
console.log(arr1);
var arr2 = arr.filter(function (item, index) {
return item > 4;
})
console.log(arr2);
1. Function.prototype.bind(obj) :
* 作用: 将函数内的this绑定为obj, 并将函数返回
2. 面试题: 区别bind()与call()和apply()?
* 都能指定函数中的this
* call()/apply()是立即调用函数
* bind()是将函数返回
示例代码:
var obj = {username: 'kobe'};
function foo(data){
console.log(this, data);
}
// 直接调用foo方法打印的this是Window
// foo();
// call和apply的方法作用一样,区别在于传参的方式
foo.call(obj, 33);// 直接从第2个参数开始,依次传入
foo.apply(obj, [33]);// 第二个参数必须是数组,参数放在数组里
// bind的特点:绑定完this不会立即调用当前的函数,而是将函数返回
var bar = foo.bind(obj);
bar();
// bind的传参方式和call一样
foo.bind(obj, 33)();
// 应用举例
setTimeout(function(){
console.log(this);
}.bind(obj), 1000);
1. 作用:
* 与var类似, 用于声明一个变量
2. 特点:
* 在块作用域内有效
* 不能重复声明
* 不会预处理(即不能在声明之前使用,会抛出异常,而var在声明前使用值是undefined), 不存在提升
3. 应用:
* 循环遍历加监听
* 使用let取代var是趋势
示例代码:
// console.log(username);
let username = 'kobe';
// let username = 'wade';
console.log(username);
let btns = document.getElementsByTagName('button');
for(var i = 0; i < btns.length; i++){
var btn = btns[i];
btn.onclick = function () {
// 打印出来的都是3
alert(i);
}
}
// 解决方法1:用闭包函数
btns = document.getElementsByTagName('button');
for(var i = 0; i < btns.length; i++){
var btn = btns[i];
(function(i){
btn.onclick = function () {
// 打印出来的都是3
alert(i);
}
})(i)
}
// 解决方法2:用let关键字
btns = document.getElementsByTagName('button');
for(let i = 0; i < btns.length; i++){
var btn = btns[i];
btn.onclick = function () {
// 打印出来的都是3
alert(i);
}
}
1. 作用:
* 定义一个常量
2. 特点:
* 不能修改
* 其它特点同let
3. 应用:
* 保存不用改变的数据
1. 理解:
* 从对象或数组中提取数据, 并赋值给变量(多个)
2. 对象的解构赋值
let {n, a} = {n:'tom', a:12}
3. 数组的解构赋值
let [a,b] = [1, 'atguigu'];
4. 用途
* 给多个形参赋值
示例代码:
let obj = {username: 'kobe', age: 39};
// let username = obj.username;
// let age = obj.age;
let {username, age} = obj;
console.log(username, age);
let arr = [1,3,5,'abc',true];
let [,,a,b] = arr;
console.log(a, b);
function foo({username, age}){// {username, age} = obj
console.log(username, age);
}
foo(obj);
1. 模板字符串 : 简化字符串的拼接
* 模板字符串必须用 `` 包含
* 变化的部分使用${xxx}定义
示例代码:
let obj = {username: 'kobe', age: 39};
let str = '我的名字叫:' + obj.username + ',我今年的年龄是:' + obj.age;
console.log(str);
str = `我的名字叫:${obj.username},我今年的年龄是:${obj.age}`;
console.log(str);
简化的对象写法
* 省略同名的属性值
* 省略方法的function
* 例如:
let x = 1;
let y = 2;
let point = {
x,
y,
setX (x) {this.x = x}
};
* 作用: 定义匿名函数
* 基本语法:
* 没有参数: () => console.log('xxxx')
* 一个参数: i => i+2
* 大于一个参数: (i,j) => i+j
* 函数体不用大括号: 默认返回结果
* 函数体如果有多个语句, 需要用{}包围,若有需要返回的内容,需要手动返回
* 使用场景: 多用来定义回调函数
* 箭头函数的特点:
1、简洁
2、箭头函数没有自己的this,箭头函数的this不是调用的时候决定的,而是在定义的时候处在的对象就是它的this
3、扩展理解: 箭头函数的this看外层的是否有函数,
如果有,外层函数的this就是内部箭头函数的this,
如果没有,则this是window。
示例代码:
// 不同传参的情况
// 1、没有形参
let fun = () => console.log('我是箭头函数');
fun();
// 2、只有1个形参,()可以省略
let fun2 = a => console.log(a);
fun2('aaa');
// 3、两个及以上形参,()不能省略
let fun3 = (x, y) => console.log(x, y);
fun3(25, 36);
// 不同函数体的情况
// 1、函数体只有一条语句或是表达式的时候,{}可以省略。会自动返回语句执行的结果或是表达式的结果
let fun4 = (x, y) => x + y;
console.log(fun4(24, 36));
// 2、函数体不止一条语句或表达式的情况下,{}不可以省略
let fun5 = (x, y) => {
console.log(x, y);
return x + y;
}
console.log(fun5(35, 50));
let obj = {
name: '箭头函数',
// getName(){
getName: () => {
btn2.onclick = () => {
console.log(this);
}
}
}
// 等价于 obj.getName = () => {},所以this是Window对象
obj.getName();
* 用途
1. rest(可变)参数
* 用来取代arguments 但比 arguments 灵活,只能是最后部分形参参数
function fun(...values) {
console.log(arguments);
arguments.forEach(function (item, index) {
console.log(item, index);
});
console.log(values);
values.forEach(function (item, index) {
console.log(item, index);
})
}
fun(1,2,3);
2. 扩展运算符
let arr1 = [1,3,5];
let arr2 = [2,...arr1,6];
console.log(arr2);
console.log(...arr2);
* 形参的默认值----当不传入参数的时候默认使用形参里的默认值
function Point(x = 1,y = 2) {
this.x = x;
this.y = y;
}
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(
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();
示例代码:
// 创建promise对象
let promise = new Promise((resolve, reject) => {
// 初始化promise状态:pending: 初始化
console.log('111');
// 执行异步操作,通常是发送ajax请求、开启定时器
setTimeout(function () {
console.log('333');
// 根据异步任务的返回结果去修改promise状态
// 异步任务执行成功
resolve('哈哈');// 修改promise的状态为 fullfilled: 成功的状态
// 异步任务执行失败
// reject('555');// 修改promise的状态为 rejected: 失败的状态
}, 2000);
})
console.log('222');
promise.then((data) => {// 成功的回调
console.log(data, '成功了!')
}, (error) => {// 失败的回调
console.log(error, '失败了。。。')
})
应用案例:
// 定义获取新闻的函数
function getNews(url){
let promise = new Promise((resolve, reject) => {
// 状态:初始化
// 执行异步任务
// 创建xmlHttp实例对象
let xmlHttp = new XMLHttpRequest();
// 绑定监听 readyState
xmlHttp.onreadystatechange = function(){
if(xmlHttp.readyState === 4){
if(xmlHttp.status == 200){// 请求成功
// 修改状态
resolve(xmlHttp.responseText);// 修改promise的状态为成功的状态
}else{// 请求失败
reject('暂时没有新闻内容');
}
}
}
// 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) => {
})
前言: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属性,指向该对象的默认遍历器方法
示例代码:
// 创建Symbol属性值
let symbol = Symbol();
console.log(symbol);
let obj = {username: 'kobe', age: 39};
obj[symbol] = 'hello';
console.log(obj);
// for in, for of遍历时不会遍历symbol属性
for(let i in obj){
console.log(i);
}
let symbol2 = Symbol('one');
let symbol3 = Symbol('two');
console.log(symbol2 == symbol3);// false
console.log(symbol2, symbol3);
// 可以去定义常量
const Person_key = Symbol('person_key');
console.log(Person_key);
概念: 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
。。。
实现模拟 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());
console.log(iteratorObj.next());
console.log(iteratorObj.next());
console.log(iteratorObj.next());
console.log(iteratorObj.next());
示例代码:
let arr = [1, 4, 65, 'abc'];
// 将iterator接口部署到指定的数据类型上,可以使用for of去循环遍历
// 数组、字符串、arguments、set容器、map容器
for(let i of arr){
console.log(i);
}
let str = 'abcdefg';
for(let i of str){
console.log(i);
}
function fun(){
for(let i of arguments){
console.log(i);
}
}
fun(1,4,5,'abc');
扩展:
// 对象的Symbol.iterator属性,指向该对象的默认遍历器方法
// 等同于在指定的数据内结构上部署了iterator接口,
// 当使用for of去遍历某一个数据结构时,首先先去找Symbol.iterator,找到了就去遍历,没有找到的话不能遍历 ==》 xxx is not iterable
let targetData = {
0: 'a',
1: 'b',
2: 'c',
length: 3,
[Symbol.iterator]: function() {
let nextIndex = 0;// 记录指针的位置
return {
next: () => {
return nextIndex < this.length ? {value: this[nextIndex++], done: false} : {value: undefined, done: true};
}
}
}
}
// 定义了Symbol.iterator之后,Object也可以用for of遍历了
for(let i of targetData){
console.log(i);
}
// 用Symbol.iterator去迭代
let numbers = [1,2,3,4,5];
let iterator = numbers[Symbol.iterator]();
console.log(iterator.next().value);
console.log(iterator.next().value);
// 使用三点运算符、解构赋值,默认去调用iterator接口
let arr2 = [1, 6];
let arr3 = [2, 3, 4, 5];
arr2 = [1, ...arr3, 6];
console.log(arr2);
let [a, b] = arr2;
console.log(a, b);
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函数返回的是指针对象,而不会执行函数内部逻辑
4、调用next方法函数内部逻辑开始执行,遇到yield表达式停止,返回{value: yield后的表达式结果/undefined, done: false/true}
5、再次调用next方法会从上一次停止时的yield处开始,直到最后
6、yield语句返回结果通常为undefined, 当调用next方法时传参内容会作为启动时yield语句的返回值。
示例代码:
function* myGenerator(){
console.log('开始执行');
let result = yield 'hello';
console.log(result);// 打印结果==》传入的参数aaa
console.log('暂停后,再次执行');
yield 'generator';
console.log('遍历完毕');
return '返回的结果';
}
let mg = myGenerator();// 返回的是指针对象
console.log(mg);
console.log(mg.next());// {value: "hello", done: false}
console.log(mg.next('传入的参数aaa'));// {value: "generator", done: false}
console.log(mg.next());// {value: "返回的结果", done: true}
// 对象的symbol.iterator属性 指向遍历器对象
let obj = {username: 'kobe', age: 39};
obj[Symbol.iterator] = function* myTest(){
yield 1
yield 2
yield 3
}
for(let i of obj){
console.log(i);
}
应用案例:
/*
* 需求:
* 1、发送ajax请求获取新闻内容
* 2、新闻内容获取成功后再次发送请求,获取对应的新闻评论内容
* 3、新闻内容获取失败则不需要再次发送请求。
* */
function getNews(url){
$.get(url, function (data){
console.log(data);
let url = 'http://localhost:3000' + data.commentsUrl;
SX.next(url);
})
}
function* sendXml(){
let url = yield getNews('http://localhost:3000/news?id=3');
yield getNews(url);
}
// 获取遍历器对象
let SX = sendXml();
SX.next();
async函数(源自ES2017)
概念: 真正意义上去解决异步回调的问题,同步流程表达异步操作
本质: Generator的语法糖
语法:
async function foo(){
await 异步操作;
await 异步操作;
}
特点:
1、不需要像Generator去调用next方法,遇到await等待,当前的异步操作完成就往下执行
2、返回的总是Promise对象,可以用then方法进行下一步操作
3、async取代Generator函数的星号*,await取代Generator的yield
4、语意上更为明确,使用简单,经临床验证,暂时没有任何副作用
示例代码:
// async基本使用
function foo(){
return new Promise(resolve => {
setTimeout(resolve, 2000);
})
}
async function test(){
console.log('开始执行', new Date().toTimeString());
await foo();
console.log('执行完毕。。。', new Date().toTimeString());
}
test();
应用案例:
// 获取新闻内容
async function getNews(url){
return new Promise((resolve, reject) => {
$.ajax({
method: 'GET',
url,
success: data => resolve(data),
error: error => reject(error)
})
})
}
async function sendXml(){
let result = await getNews('http://localhost:3000/news?id=3');
console.log(result);
result = await getNews('http://localhost:3000' + result.commentsUrl);
console.log(result);
}
sendXml();
1. 通过class定义类/实现类的继承
2. 在类中通过constructor定义构造方法
3. 通过new来创建类的实例
4. 通过extends来实现类的继承
5. 通过super调用父类的构造方法
6. 重写从父类中继承的一般方法
示例代码:
// 定义一个人物的类
class Person{
// 类的构造方法
constructor(name, age){
this.name = name;
this.age = age;
}
// 类的一般方法
showName(){
console.log(this.name, this.age);
}
}
let person = new Person('kobe', 39);
console.log(person);
person.showName();
// 子类
class StarPerson extends Person{
constructor(name, age, salary){
super(name, age);// 调用父类的构造方法
this.salary = salary;
}
// 子类的方法重写
showName(){
console.log(this.name, this.age, this.salary);
}
}
let p1 = new StarPerson('wade', 36, 100000000);
console.log(p1);
p1.showName();
1. includes(str) : 判断是否包含指定的字符串
2. startsWith(str) : 判断是否以指定字符串开头
3. endsWith(str) : 判断是否以指定字符串结尾
4. repeat(count) : 重复指定次数
示例代码:
let str = 'abc';
console.log(str.includes('t'));
console.log(str.includes('a'));
console.log(str.startsWith('a'));
console.log(str.endsWith('f'));
console.log(str.repeat(3));// abcabcabc
1. 二进制与八进制数值表示法: 二进制用0b, 八进制用0o
2. Number.isFinite(i) : 判断是否是有限大的数
3. Number.isNaN(i) : 判断是否是NaN
4. Number.isInteger(i) : 判断是否是整数
5. Number.parseInt(str) : 将字符串转换为对应的数值
6. Math.trunc(i) : 直接去除小数部分
示例代码:
console.log(0b1010);// 10
console.log(0o56);// 46
console.log(Number.isFinite(Infinity));// false
console.log(Number.isNaN(NaN));// true
console.log(Number.isInteger(123.12));// false
console.log(Number.isInteger(123.0));// true
console.log(Number.parseInt('123abc222'));// 123
console.log(Number.parseInt('abc222'));// NaN
console.log(Math.trunc(123.12));// 123
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的元素下标
示例代码:
let btns = document.getElementsByTagName('button');
console.log(btns);
Array.from(btns).forEach(function(item, index){
console.log(item);
})
let arr = Array.of(1,3,'abc',true);
console.log(arr);
let arr2 = [2,3,4,2,5,7,3,6,5];
let result = arr2.find(function(item, index){
return item > 4;
})
console.log(result);
result = arr2.findIndex(function(item, index){
return item > 4;
})
console.log(result);
1. Object.is(v1, v2)
* 判断2个数据是否完全相等(其实是用字符串比较)
2. Object.assign(target, source1, source2..)
* 将源对象的属性复制到目标对象上
3. 直接操作 __proto__ 属性
let obj2 = {};
obj2.__proto__ = obj1;
示例代码:
console.log(0 == -0);// true
console.log(NaN == NaN);// false
console.log(Object.is(0, -0));// false
console.log(Object.is(NaN, NaN));// true
let obj = {};
let obj1 = {username: 'iverson', age:42};
let obj2 = {sex: '男'};
Object.assign(obj, obj1, obj2);
console.log(obj);
let obj3 = {};
let obj4 = {qian: 5000000};
obj3.__proto__ = obj4;
console.log(obj3);
console.log(obj3.qian);
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). 深拷贝包含函数数据的对象/数组
示例代码:
// 不会影响原数据
let str = 'abcd';
let str2 = str;
console.log(str2);
str2 = '';
console.log(str);
let bool1 = true;
let bool2 = bool1;
bool2 = false;
console.log(bool1);
// 拷贝数组/对象,没有生成新的数据而是复制了一份引用
let obj = {username: 'kobe', age: 39};
let obj2 = obj;
console.log(obj2);
obj2.username = 'wade';
console.log(obj.username);
let arr = [1, 4, {username: 'kobe', age: 39}];
let arr2 = arr;
arr2[0] = 'abcd';
console.log(arr, arr2);
/**
* 拷贝数据:
* 基本数据类型:
* 拷贝后会生成一份新的数据,修改拷贝以后的数据不会影响原数据
* 对象/数组:
* 拷贝后不会生成新的数据,而是拷贝的引用。修改拷贝以后的数据会影响原来的数据
*
* 拷贝数据的方法:
* 1.直接赋值给一个变量 // 浅拷贝
* 2.Object.assign() // 浅拷贝
* 3.Array.prototype.concat() // 浅拷贝
* 4.Array.prototype.slice() // 浅拷贝
* 5.JSON.parse(JSON.stringify()) // 深拷贝(深度克隆),但不能处理函数数据
*
* 浅拷贝(对象/数组)
* 特点:拷贝的引用,修改拷贝以后的数据会影响原数据,使得原数据不安全
* 深拷贝(深度克隆)
* 特点:拷贝的时候生成新数据,修改拷贝以后的数据不会影响原数据
*/
{
let obj = {username: 'kobe'};
let obj2 = Object.assign(obj);
console.log(obj2);
obj.username = 'wade';
console.log(obj2);
let arr = [1, 3, {username: 'kobe'}, function fun(){}];
let arr2 = arr.concat();
arr2[0] = 2;// 这个修改不影响arr的第一个元素,因为是复制的基本类型
arr2[2].username = 'wade';// 这个修改会影响原数据,因为复制是对象的引用
console.log(arr);
let arr3 = arr.slice();
arr3[2].username = 'iverson';// 会影响
console.log(arr);
let arr4 = JSON.parse(JSON.stringify(arr));
console.log(arr4);
arr4[2].username = 'duncan';// 不影响
console.log(arr, arr4);
实现深度克隆前的知识储备:
/**
* 思考:如何实现深度拷贝(克隆)
* 即使有对象/数组,也可以继续遍历对象/数组拿到里边每一项值,直到拿到的是基本数据类型,然后再去复制,就是深度拷贝
*
* 如何判断数据类型:arr --> Array null --> Null
* 1.typeof返回的数据类型:String, Number, Boolean, Undefined, Object, Function
* 2.Object.prototype.toString.call(this)
*/
let result= 'abcd';
result = null;
result = [1, 3];
console.log(Object.prototype.toString.call(result).slice(8, -1));
// for in循环,对象(属性名) 数组(下标)
let obj6 = {username: 'kobe', age: 39};
for (let i in obj6){
console.log(i);
}
let arr6 = [1, 3, 'abc'];
for (let i in arr6){
console.log(i);
}
实现深度克隆:
// 定义检测数据类型的功能函数
function checkType(target){
return Object.prototype.toString.call(target).slice(8, -1);
}
// 实现深度克隆 --> 对象/数组
function clone(target){
// 判断拷贝的数据类型
// 初始化变量result成为最终克隆的数据
let result, targetType = checkType(target);
if(targetType === 'Object'){
result = {};
}else if(targetType === 'Array'){
result = [];
}else{
return target;
}
// 遍历目标数据
for(let i in target){
// 获取遍历数据结构的每一项值
let value = target[i];
// 判断目标结构里的每一项值是否存在对象/数组
if(checkType(value) === 'Object' || checkType(value) === 'Array'){// 对象/数组里嵌套了对象/数组
// 继续遍历获取到的value值
result[i] = clone(value);
}else{// 获取到的value值是基本的数据类型或函数
result[i] = value;
}
}
return result;
}
let arr7 = [1, 2, {username: 'kobe', age:39}];
let arr8 = clone(arr7);
console.log(arr8);
arr8[2].username = 'wade';
console.log(arr7, arr8);
let obj7 = {username: 'kobe', age: 39};
let obj8 = clone(obj7);
console.log(obj8);
obj8.username = 'wade';
console.log(obj7, obj8);
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
示例代码:
let set = new Set([1,2,4,5,2,3,6]);
console.log(set);
set.add(7);
console.log(set.size, set);
console.log(set.has(8));// false
console.log(set.has(7));// true
set.delete(4);
console.log(set);
set.clear();
console.log(set);
let map = new Map([['username', 'kobe'], [36, 'age']]);
console.log(map);
map.set(78, 'haha');
console.log(map);
map.delete(36);
console.log(map);
console.log(map.size);
for(let value of target){}循环遍历
1. 遍历数组
2. 遍历Set
3. 遍历Map
4. 遍历字符串
5. 遍历伪数组
示例代码:
// 数组去重
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);
// 遍历伪数组
let btns = document.getElementsByTagName('button');
for(let i of btns){
console.log(i);
}
1. 指数运算符(幂): **
2. Array.prototype.includes(value) : 判断数组中是否包含指定value
示例代码:
console.log(3 ** 3);// 27
let arr = [1,4,5,6,'abc'];
console.log(arr.includes(6));// true