js由三部分组成:ES+DOM
(文档对象模型)+?BOM
(浏览器对象模型)
ES作为核心,是一套标准,规范了语言的组成部分:语法、类型、语句、关键字、保留字。定义了数据结构和语法,定义了函数和对象的实现,包括原型链和作用域链的机制和实现。
JavaScript
?的核心?ECMAScript
?描述了该语言的语法和基本对象;
DOM
?描述了处理网页内容的方法和接口;
BOM
?描述了与浏览器进行交互的方法和接口;
关于ES6详细的介绍请查看:《ECMAScript 6标准入门》
let
?是?ES6
?新引入的声明变量的方式,特点:
1. 不能重复声明
let star = '123'; let star = '123';//Identifier 'star' has already been declared
2. 块级作用域,仅在代码块内生效。var
是全局作用域
3. 不存在变量提升
console.log(name); let name = '世界你好';// Cannot access 'name' before initialization
const
?相当于常量
1.一定要赋初始值
const A = 1
2. 一般常量要大写
const A = 100; console.log(A)
3.常量的值不能修改
A = 200//Assignment to constant variable.
4.const也是块级作用域
{ const PLAYER = '李四'; } console.log(PLAYER);//PLAYER is not defined
5.对数组和对象的对象修改,不算对常量的修改,不会报错,建议用?const
声明数组和对象
const FOOL = ['苹果','西瓜','水蜜桃','李子']; FOOL.push('草莓'); console.log(FOOL);
ES6允许按照一定的模式从数组或对象中提取值,对变量进行赋值,称之为变量的解析赋值
1.数组的结构
const F4 = ['刘能','赵四','小沈阳','宋小宝']; let [liu,zhao,xiao,song] = F4; console.log(liu); console.log(zhao); console.log(xiao); console.log(song);
2.对象的结构
const Person = { name: '张三', age: 12, learn: function(){ console.log('努力学习,找到好工作'); } } let {name, age, learn} = Person; console.log(name); console.log(age); console.log(learn); learn();
ES6允许在大括号里,直接写入变量和函数,作为对象的属性和方法。
let name = '张三'; let learn = function(){ console.log('张三在学习'); } const person = { name, learn, dosometing(){ console.log('做点什么比较好'); } } console.log(person); person.dosometing(); person.learn();
ES6允许使用箭头?=>
?定义函数
原始函数的声明:
let fn = function (a,b){ xxxxxx return xxx; }
使用箭头函数声明:
let fn = (a,b) =>{ return a + b; } // 函数调用 let result = fn(3,3) console.log(result)
使用规范:
1.?this
?是静态的。this始终指向函数声明时所在作用域下的?this
?的值
function getName(){ console.log(this.name); } let getName2 = ()=>{ console.log(this.name); } window.name = 'hello ES6'; const dog = { name: "你好" } //直接调用 getName(); getName2(); //call方法调用 getName.call(dog); getName2.call(dog);
2. 不能作为构造函数实例化对象
let person = (name,age) => { this.name = name; this.age = age; } let someone = new person('zhangsan',12); console.log(someone);//Uncaught TypeError: person is not a constructor
3. 不能使用?argument
?变量
let fn = () =>{ console.log(arguments); } fn(1,2,3);
4. 箭头函数简写
1 2 3 4 5 6 7 8 9 |
|
ES6允许函数参数赋值初始值
1. 形参初始值 具有默认值的参数,一般位置靠后(约定俗成)
function add (a,b,c=10){ return a + b + c; } let result =add(1,2) console.log(result)
2. 与解构赋值结合,也可以进行初始化赋值
function connect ({host = '127.0.0.1',username,password,port}){ console.log(host); console.log(username); console.log(password); console.log(port); } connect({ // host:'localhost', username:'root', password:'root', port:3306 })
ES6引入?rest
?参数,用于获取函数的实参,用来代替argument
1. ES5获取实参的方式
function data (){ console.log(arguments)//arguments是一个对象 } data('dog','cat','pig')
2. ES6的?rest
?参数,是一个数组,可以使用数组相关的api如:filter
?some
?every
?map
等
function data (...args){ console.log(args) } data('dog','cat','pig')
3.?rest
?参数必须放到参数最后
function data (...args){ console.log(args) } data('dog','cat','pig')
...
?扩展运算符能将【数组】转化为逗号分隔的【参数序列】
声明数组
const F4 = ['刘能','赵四','小沈阳','宋小宝'];
声明函数
function paozao(){ console.log(arguments); } paozao(...F4)//相当于paozao('刘能','赵四','小沈阳','宋小宝')
扩展运算符的应用
1.数组的合并
const hello = ['h','e','l','l','o']; const word = ['w','o','r','d']; const helloword = hello.concat(word); console.log(helloword)
2. 数组的克隆
const copy = ['c','o','p','y']; const copy2 = [...copy] console.log(copy2)
3. 将伪数组转化为真正的数组
const divs = document.querySelectorAll('div'); console.log(divs); const divAll = [...divs]; console.log(divAll);
Symbol
是一种新的原始数据类型,表示独一无二的值,时javascript语言的第七种数据类型,是一种类似于字符串的数据类型。
? 1. Symbol的值是唯一的,用来解决命名冲突的问题
? 2. Symbol值不能与其他的数据进行运算
? 3.Symbol定义的对象属性不能使用?for...in
?遍历循环,但是可以使用?Reflect.ownKeys
?来获取对象的所有键名。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
不能与其他数据进行运算
let result = s1 + 100;//Cannot convert a Symbol value to a number let result = s1 > 100;//Cannot convert a Symbol value to a number let result = s1 + '123';//Cannot convert a Symbol value to a string let result = s1 + s1;//Cannot convert a Symbol value to a number
js常见数据类型总结:USONB: you are so niubility
声明对象
let methods = { up:Symbol(), down: Symbol() }; game[methods.up] = function(){ console.log('我可以上升') } game[methods.down] = function(){ console.log('我可以下降') } console.log(game) let lol = { name:"英雄联盟", [Symbol('yasuo')]:function(){ console.log("快乐风男"); }, [Symbol('jiansheng')]:function(){ console.log("无极剑圣") } } console.log(lol)
Symbol内置属性,提供了11种内置属性
是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署iterator接口就可以完成遍历操作
ES6提供了一种新的遍历命令?for...of
?循环,iterator接口主要为?for...of
?服务
原生具备 Iterator接口的数据(可用 for of遍历)
3.工作原理
? a.创建一个指针对象,指向当前数据结构的起始位置
? b.第一次调用对象的next方法,指针自动指向数据结构的第一个成员
? c.接下来不断调用next方法,指针一直往后移动,直到指向最后一个成员
? d.每调用next方法返回一个包含 value和done属性的对象
注:需要自定义遍历数据的时候,要想到迭代器。
const person = ['张三','李四','王五']; for(let v of person){ console.log(v);//'张三','李四','王五' } for(let v in person){ console.log(v);//0,1,2 } for...of保存的键值,for...in保存的键名 let iterator = person[Symbol.iterator](); //调用对象的next方法 console.log(iterator.next()); console.log(iterator.next()); console.log(iterator.next()); console.log(iterator.next());
迭代器自定义遍历对象
const banji = { name:"终极一班", stus:[ 'xiaoming', 'xiaohong', 'xiaotian', 'xioahu' ], [Symbol.iterator](){ let index = 0; let _this = this; return{ next:function(){ if(index < _this.stus.length){ const result = {value: _this.stus[index], done: false}; index++; return result; }else{ return {value: undefined, done: true}; } } }; } } //遍历对象,要求用for...of遍历,且返回的为对象中数组的成员 for(let v of banji){ console.log(v) }
生成器函数是ES6提供的一种异步编程解决方案,语法行为与传统函数完全不同实际上就是一种特殊的函数,进行异步编程
声明方式,在function后面加?*
,调用时调用next()方法执行
生成器实现了iterator属性带有?next()
?方法
yield语句函数代码的分隔符
function * gen(){ console.log("hello generator") console.log(111); yield '一只没有耳朵'; console.log(222); yield '一直没有尾巴'; console.log(333); yield '真奇怪'; console.log(444); } let iterator = gen(); // console.log(iterator) iterator.next(); iterator.next(); iterator.next(); iterator.next(); //遍历 for(let v of gen()){ console.log(v); } //生成器函数参数 function * gen(args){ console.log(args) let one = yield 111; console.log(one); yield 222; yield 333; } let iterator = gen('AAA'); console.log(iterator.next()); //next()方法可以传入实参,参数作为上一个yield语句返回结果 console.log(iterator.next('BBB')); console.log(iterator.next()); console.log(iterator.next());
异步编程(js是单线程执行的,操作需要异步执行) 文件读取 网络操作(ajax
,request
) 数据库操作
生成器函数示例-1
1S后控制台输出111 2S后输出222 3S后输出333
使用定时器的方式实现,回调地狱
setTimeout(() => { console.log(111); setTimeout(() => { console.log(222); setTimeout(() => { console.log(333); }, 3000); }, 2000); }, 1000);
生成器的方式实现
function one (){ setTimeout(() =>{ console.log(111); iterator.next() },1000) } function two (){ setTimeout(() =>{ console.log(222); iterator.next() },1000) } function three (){ setTimeout(() =>{ console.log(333); iterator.next() },1000) } function * gen (){ yield one(); yield two(); yield three(); } let iterator = gen(); iterator.next();
生成器函数示例-2
模拟获取(依次获取) 用户数据 订单数据 商品数据
function getUsers (){ setTimeout(() =>{ let data = '获取用户数据' // 调用next()方法,并将数据传入 iterator.next(data); },1000) } function getOrders (){ setTimeout(() =>{ let data = '获取订单数据' iterator.next(data); },1000) } function getGoods (){ setTimeout(() =>{ let data = '获取商品数据' iterator.next(data); },1000) } function * gen (){ let users = yield getUsers(); console.log(users); let orders = yield getOrders(); console.log(orders); let goods = yield getGoods(); console.log(goods); } let iterator = gen(); iterator.next()
Promise是ES6引入的异步编程的新解决方案。语法上 Promise是一个构造函数,用来封装异步操作并可以获取其成功或失败的结果
Promise构造函数:?Promise( executor){}
Promise.prototype.then
?方法
Promise.prototype.catch
?方法
使用方法
//实例化对象 const p = new Promise(function(resolve, reject){ setTimeout(function(){ let data = '数据库中的数据'; resolve(data); let err = '读取数据失败'; reject(err); },1000) }); // 调用Promise对象的then方法 p.then(function(value){ console.log(value); },function(reason){ console.error(reason); })
1. Promise封装读取文件
const fs = require('fs'); const p = new Promise(function(resovle,reject){ fs.readFile("./resources/test.txt",(err,data)=>{ // 判断失败 if (err) reject(err); // 成功 resovle(data); }); }); p.then(function(value){ console.log(value.toString()); },function(reason){ console.error('操作失败'); })
2. Promise发送ajax请求
const p = new Promise((resolve,reject) => { // 1.创建对象 const xhr = new XMLHttpRequest(); // 2.初始化 xhr.open("GET","https://api.apiopen.top"); // 3.发送 xhr.send(); // 4.绑定事件,处理响应结果 xhr.onreadystatechange = function (){ // 处理逻辑 if(xhr.status >= 200 && xhr.status < 300){ // 响应成功 resolve(xhr.response); }else{ // 响应失败 reject(xhr.status); } } }) // 指定回调函数 p.then(function(value){ console.log(value) },function(reason){ console.log(reason) })
3. Promise的then()方法
1 2 3 4 5 |
|
then指定回调函数,then方法的返回结果是 Promise对象,对象状态由回调函数的执行结果决定
如果回调函数中返回的结果是非 promise类型的属性,状态为成功,返回值为对象的成功的值
const result = p.then(value =>{ console.log(value); 1.非Promise类型的属性 return "good"; 2.Promise类型的属性 return new Promise((resolve,reject) =>{ resolve('ok'); // reject('error'); }); 3.抛出异常 throw new Error('出错了!'); },reason =>{ console.warn(reason); }); console.log(result);
then()方法支持链式调用,链式调用可以解决回调地狱的问题
p.then(value=>{},reason=>{}).then(value={},reason=>{})
4. Promise-读取多个文件
const fs = require('fs'); 使用回调地狱的方式实现 fs.readFile('./resources/test.txt', (err, data1) => { fs.readFile('./resources/test1.txt', (err, data2) => { fs.readFile('./resources/test3.txt', (err, data3) => { let result = data1 + '\r\n' + data2 + '\r\n' + data3; console.log(result); }); }); }); // 采用Promise对象的方式实现多文件读取,采用链式调用的方式进行拼接操作 const p = new Promise((reslove,reject)=>{ fs.readFile('./resources/test.txt',(err,data)=>{ reslove(data); }); }); p.then(value=>{ return new Promise((reslove,reject)=>{ fs.readFile('./resources/test1.txt',(err,data)=>{ reslove([value,data]); }); }); }).then(value=>{ return new Promise((reslove,reject)=>{ fs.readFile('./resources/test3.txt',(err,data)=>{ value.push(data); reslove(value); }); }); }).then(value=>{ console.log(value.join('\r\n')); })
Promise的?catch()
?方法,用于指定Promise对象失败时的回调是语法糖,相当于没有指定value参数的?then()
?方法
const p = new Promise((reslove,reject)=>{ setTimeout(() => { reject('出错啦!'); }, 1000); }); // 通过then()方法的reason指定失败时的操作 p.then(value=>{},reason=>{ console.error(reason); }); //通过catch()方法获取Promise对象的异常 p.catch(reason=>{ console.warn(reason); })
ES6提供了新的数据结构Set(集合)。它类似于数组,但成员的值都是唯一的,集合实现了 Iterator接口,所以可以使用扩展运算符...
和?for...of...
?进行遍历,
集合的属性和方法:
size 返回集合的元素个数
add 增加一个新元素,返回当前集合
delete 删除元素,返回 boolean值
has 检测集合中是否包含某个元素,返回 boolean值
//声明一个set let s = new Set(); let s2 = new Set([12,13,14,15,21,12]); // console.log(s ,typeof s); console.log(s2); // 1) size 返回集合的元素个数 console.log(s2.size); // 2) add 增加一个新元素,返回当前集合 s2.add(100); console.log(s2); // 3) delete 删除元素,返回 boolean值 s2.delete(15); console.log(s2); // 4) has 检测集合中是否包含某个元素,返回 boolean值 console.log(s2.has(100)); // 5) clear 清空集合 s2.clear(); console.log(s2); // 6)for..of..遍历 for(v of s2){ console.log(v); } //set集合的使用 let arr = [1, 2, 3, 4, 5, 4, 3, 2, 1]; let arr2 = [4,5,6,5,6]; //1.数组去重 let result = [...new Set(arr)]; console.log(result); //2.交集 let result = [...new Set(arr)].filter(item =>{ let s2 = new Set(arr2); if(s2.has(item)){ return true; }else{ return false; } }); console.log(result); let result = [...new Set(arr)].filter(item => new Set(arr2).has(item)); console.log(result); //3.并集 let union = [...new Set([...arr,...arr2])]; console.log(union); //4.差集 let diff = [...new Set(arr)].filter(item => !(new Set(arr2).has(item))); console.log(diff);
ES6提供了更接近传统语言的写法,引入了class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。
基本上,ES6的 class 可以看作只是一个语法糖,它的绝大部分功能,ES5都可以做到,
新的 class写法只是让对象原型的写l法更加清晰、更像面向对象编程的语法而己。
知识点:
class 声明类
constructor 定义构造函数初始化
extends 继承父类
super 调用父级构造方法
static 定义静态方法和属性
? ? ? ? ?6.父类方法可以重写
//使用class()声明类 class Computer { // 构造方法,名字不可修改,会在实例化对象的时候自动调用,可以没有 constructor(brand, price) { this.brand = brand; this.price = price; } // 定义方法,不能使用ES5的方式声明 start() { console.log('欢迎使用') } } // 实例化对象 let lenovo = new Computer('lenovo', 5499); console.log(lenovo) lenovo.start();
class中的静态成员,属于类而不属于实例化对象
class Phone { // 静态属性 static name = '手机' // 静态方法 static call() { console.log('我能打电话'); } } let huawei = new Phone; console.log(huawei.name);//undifined console.log(Phone.name);//手机 Phone.call();//我能打电话 huawei.call();//huawei.call is not a function
使用构造函数实现继承
//父类对象 class Phone { constructor(brand, price) { this.brand = brand; this.price = price; } call() { console.log('打电话'); } } // 子类继承父类 class SmartPhone extends Phone { constructor(brand, price, color, size) { super(brand, price); this.color = color; this.size = size; } photo() { console.log('拍照'); } playGame() { console.log('玩游戏'); } // 子类重写父类的方法 call() { console.log('视频通话'); } } const xiaomi = new SmartPhone('小米6', 1999, '白色', '5.15inch'); console.log(xiaomi); // 子类不能调用父类的同名方法 xiaomi.call(); xiaomi.photo(); xiaomi.playGame();
class中get和set方法
class Phone{ // get对动态属性进行封装 get price (){ console.log('get方法被执行'); return 'get get get' } // set属性可以进行属性的判断和封装,需要设置形参 set price (newVal){ console.log('set方法执行'); console.log('新的价格为' + newVal); } } let p = new Phone(); console.log(p.price); p.price = 100;
1.?Number. EPSILON
?是 JavaScript表示的最小精度EPSILON属性的值接近于2.2264466492503136884726361816E-16?
多用于浮点数运算,用于解决浮点数计算误差问题
function equals(a, b) { if (Math.abs(a - b) < Number.EPSILON) { return true; } else { return false; } } console.log(0.1 + 0.2 === 0.3); console.log(equals(0.1 + 0.2, 0.3));
2. 二进制和八进制
let b = 0b1010;//二进制 let o = 0177;//八进制 let d = 0x123;//16进制 console.log(b); console.log(o); console.log(d);
3.?Number.isFinite
?检测一个数值是否为有限数
console.log(Number.isFinite(100));//true console.log(Number.isFinite(100/0));//false
4.?Number.isNaN
?检测一个数值是否为?NaN
console.log(Number.isNaN(2));//false console.log(Number.isNaN(NaN));//true
5.?Number.parseInt
?Number.parserFloat?
字符串转整数
onsole.log(Number.parseInt('1323.1dcsdcd'));//1323 console.log(Number.parseFloat('13231.12dcsdcd'));//13231.12
6.?Number.isInteger
?判断一个数是否为整数
console.log(Number.isInteger(213.3));//false console.log(Number.isInteger(213));//true
7.Math.trunc
?将数字的小数部分抹掉
console.log(Math.trunc(12.3));
8.?Math.sign
?判断一个数到底为正数 负数 还是零
1 2 3 |
|
1.?Object.is
?判断两个值是否完全相等,类似于?===
?但是可以判断?NaN
?是否相等
console.log(Object.is(110,110));//true console.log(Object.is(110,10));//false console.log(Object.is(NaN,NaN));//true console.log(NaN === NaN);//false
2.?Object.assign
?对象的合并
const config1 = { host: '127.0.0.1', port:'3306', user: 'user', password: '123456', test: 'test' } const config2 = { host: '49.123.124.5', port:'3306', user: 'root', password: 'root', enviroment: 'dev' } console.log(Object.assign(config1,config2));
3.Object.setPrototypeof
?Object.getPrototypeof
const school = { name: 'SDUT' } const city = { student: ['山东', '河北', '内蒙', '贵州', '杭州'] } Object.setPrototypeOf(school, city); console.log(Object.getPrototypeOf(school)); console.log(school);
模块化:将大的程序文件,拆分成很多小的文件(模块),然后将小的文件组合起来
好处:防止命名冲突 代码复用 便于维护.
ES6模块化语法:
模块功能主要由两个命令构成:?export
?和?import
export
?命令用于规定模块的对外接口
Import
?命令用于输入其他模块提供的功能
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>ES6模块化测试</title> </head> <body> <!-- 标签引入m1.js模块 --> <script type="module"> //1.通用导入方式 // 引入m1.js模块 import * as m1 from "./js/m1.js"; console.log(m1); console.log(m1.learn()); // 引入m2.js模块 import * as m2 from "./js/m2.js"; console.log(m2); console.log(m2.study()); // 引入m3.js模块 import * as m3 from "./js/m3.js"; console.log(m3); m3.default.change(); //2.解构赋值 import {name,learn} from './js/m1.js'; console.log(name); console.log(learn); // as使用别名的方式应用 import{name as lisi,study} from './js/m2.js'; console.log(lisi); console.log(study); // 引入默认暴露 import {default as m3} from './js/m3.js'; console.log(m3); //3.简便形式,只针对默认暴露 import m3 from './js/m3.js'; console.log(m3); </script> <!-- 外部引入模块 --> <script src="./js/app.js" type="module"></script> </body> </html>