模块化导入与导出
ES6导入与导出:
1、列表导入导出
//列表导出
exports.firstName = firstName;
exports.lastName = lastName;
//列表导入
import {firstName,lastName} from '路径';
2、重命名导入导出
//重命名导出
exports.first = firstName;
exports.last = lastName;
//重命名导入
import {first as f,last as l} from './3-modules';
3、单个属性导出导入
//单个属性导出
var a = exports.a = 1;
//单个属性导入
import {a} from './3-modules';
4、方法导入导出
//方法导出
exports.get = get;
function get() {
console.log('modules3导出');
};
//方法导入
import {get} from './3-modules';
5、默认导入导出
//默认导出
exports.default = {
firstName: firstName,
lastName: lastName,
b: '默认导出'
};
//默认导入
import person from './3-modules';
console.log(person,'默认导入');
**注意:**一个模块只能有一个默认导出
ES5导入与导出:
commonjs模块化导出
let firstName='ren';
let lastName='terry';
// module表示当前模块对象
// console.log(module,'当前模块对象');
module.exports={
firstName,
lastName
}
commonjs模块化导入
let {firstName,lastName} =require('./5-modules');
console.log(firstName,lastName,'modules6打印');
ES6和ES5模块化导入导出的区别:
1、commonjs在运行时加载,es6在编译时加载
2、commonjs输出的是值的复制,而es6输出的是一个值的引用。
let、const、var的区别
- 块级作用域:let、const具有块级作用域,var不具有块级作用域。
- 给全局添加属性: 浏览器的全局对象是window,Node的全局对象是global。var声明的变量为全局变量,并且会将该变量添加为全局对象的属性,但是let和const不会。
- 变量声明:var声明变量,可以重复声明,后声明的变量覆盖前声明的变量,let、const不可以重复声明。
- 初始值设置:在变量声明时,var 和 let 可以不用设置初始值。而const声明变量必须设置初始值,常用于规定值,如PI。
- 变量提升: var存在变量提升,let和const不存在变量提升,即在变量只能在声明之后使用,否在会报错。
- 暂时性死区: 在使用let、const命令声明变量之前,该变量都是不可用的。这在语法上,称为暂时性死区。使用var声明的变量不存在暂时性死区。
- 指针指向: let和const都是ES6新增的用于创建变量的语法。 let创建的变量是可以更改指针指向(可以重新赋值)。但const声明的变量是不允许改变指针的指向。
解构
定义:按照一定的模式,从数组和对象中提取值,对变量进行赋值,这被称为解构。
**注意:**解构的本质属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值。如果解构不成功,变量的值就等于undefined。
数组解构
- 第一种:完全解构
let [a,b,c]=[1,2,3];
console.log(a,b,c);//1 2 3
let [a,b,c]=[1,2,3];
console.log(a,b,c,d);//1 2 3 undefined
let [a,b,c,d,e]=[1,2,3,[4,5],6];
console.log(a,b,c,d,e);//1 2 3 [ 4, 5 ] 6
- 第二种:不完全解构
let [a,b,c,[d],e]=[1,2,3,[4,5,6],7];
console.log(a,b,c,d,e);//1 2 3 4 7
- 第三种:默认值解构
//默认值生效条件 当右侧匹配严格模式为undefiend
let [a=1,b=2,c=3]=[4,5,6];
console.log(a,b,c);//4 5 6
// 默认值也可以是一个函数
let test=()=>{
console.log('我是箭头函数');
return 1
}
let [a=test()]=[];
console.log(a);//我是箭头函数 1
- 第四种:集合解构 拓展运算符
let [a,...b]=[1,2,3,4];
console.log(a,b);//1 [ 2, 3, 4 ]
- 第五种:拓展运算符
let a=[1,2,3,4,5];
let [...arr]=a;
console.log(arr);//[ 1, 2, 3, 4, 5 ]
console.log(arr===a);//false
对象解构
- 属性名必须和变量名一致才能取到正确的值
let {name,age}={name:'zhangsan',age:12};
console.log(name,age);//zhangsan 12
- 重命名解构
// 2.属性名和变量名不一致 给属性名重命名
let {name:a,age:b}={name:'zhangsan',age:12};
console.log(a,b);//zhangsan 12
- 嵌套解构
let obj={p:['hello',{y:"world"}]};// a b取到hello world
let {p:[a,{y:b}]}=obj;
console.log(a,b);//hello world
- 对象默认值结构
let {x:y=8}={};
console.log(y);//8
字符串解构
- 使用数组进行字符串解构
let [a,b,c,d,e]='hello';
console.log(a,b,c,d,e);//h e l l o
- 使用拓展运算符 解构字符串
let [...arr]='world';
console.log(arr);//[ 'w', 'o', 'r', 'l', 'd' ]
- 使用对象解构字符串
//相当于把‘hello’当成String基本包装器类型
let {toString,valueOf,length}='hello';
console.log(toString,valueOf,length)//[Function: toString] [Function: valueOf] 5
箭头函数
概念:在es6种允许使用箭头函数,箭头函数提供了更加简洁的函数书写方式,箭头函数多用于匿名函数的定义;
箭头函数的注意点:
1、如果形参只有一个,则小括号可以省略。
2、函数体如果只有一条语句,则花括号可以省略,并省略return,函数的返回值为该条件语句的执行结果。
3、箭头函数this指向声明时所在作用域下this的值。
4、箭头函数不能作为构造函数实例化;
5、不能使用argument
特性
1、箭头函数的this时静态的,始终指向函数声明所在作用域下的this的值
2、不能作为构造实例化对象
3、不能使用argument变量,要使用reat参数。
函数的写法
- es5函数
sayName:function(){
函数体
}
- es6函数简写
sayName(){
console.log(this.name);
}
- es6箭头函数
sayName:()=>{
console.log('111');//undefined
}
reat参数
返回一个保存实参的数组,搭配拓展运算符使用。
看下面的例子:
let test=(a,...b)=>{
console.log(a,b); //1 [2,3]
// console.log(arguments,'222');
// es6 箭头函数 arguments不再保存实参列表
}
test(1,2,3)
箭头函数和普通函数的区别?
- 语法更加简洁、清晰
- 箭头函数是匿名函数,不能作为构造方法,不能使用new关键字
- 箭头函数不能使用argument,而用reat参数
- 箭头函数没有自己的this,会捕获上下文的this,并且不能通过call()、apply()来改变其this指向
- 箭头函数没有原型
拓展运算符
对象的扩展运算符
拓展运算符用到左侧是聚合,右侧是展开。
对象中拓展运算符用于去除参数对象中所有可遍历属性,拷贝到当前对象中
看以下的例子就能明白。
- 在右侧时,是实现对象的合并。其实就是实现了Object.assign方法,如果有重复的属性值,会被覆盖
let obj={name:'zhangsan',age:12};
let obj1={gender:"male"};
let temp={
...obj,
...obj1,
}
temp.width='100px';
console.log(temp);//{ name: 'zhangsan', age: 12, gender: 'male', width: '100px' }
let obj={name:'zhangsan',age:12};
let obj1={gender:"male"};
let temp=Object.assign(obj,obj1,{name:'lisi'})//{ name: 'lisi', age: 12, gender: 'male' }
- 在左侧时,拓展运算符实现了深拷贝,修改obj4中的属性值,不会导致obj3中的属性值改变,因为拓展运算符实现的是值的引用。
let obj3 = {
name:'zhangsan',
age:18,
gender:'male'
}
let {...obj4} = obj3
obj3.name='sintop'
console.log(obj4,obj3);//{ name: 'zhangsan', age: 18, gender: 'male' } { name: 'sintop', age: 18, gender: 'male' }
数组的拓展运算符
- 可以将数组转换为参数序列
function Add(a,b){
return a+b
}
let number = [4,6]
console.log(Add(...number));//10
- 可以复制数组,直接复制数组是实现了浅拷贝,会导致第一个数组的值发生改变,第二个数组的值也会被同时改变。所以利用运算符来实现深拷贝数组。
let arr=[1,2,3,4];
let [...a]=arr;
console.log(a);//[1,2,3,4]
- 扩展运算符可以与解构赋值结合起来,用于生成数组
const [first, ...rest] = [1, 2, 3, 4, 5];
console.log(first); // 1
console.log(rest); // [2, 3, 4, 5]
注意:如果将拓展运算符用于数组赋值,只能放在参数的最后一位,否则会报错
- 扩展运算符还可以将字符串转为真正的数组
let str = 'hello'
let res = [...str]
console.log(res);//[ 'h', 'e', 'l', 'l', 'o' ]
对象api拓展
静态方法
- is 比较两个数是否相等 返回值:true 或者是false
console.log(Object.is(+1,-1));//false
console.log(Object.is(+0,-0));//false
console.log(+0===-0);//true
console.log(NaN===NaN);//false
console.log(Object.is(NaN,NaN));//true
-assign
两个参数 复制/拷贝 返回值:返回第一个参数
使用场景 :
深拷贝 指得是被复制得对象里是基本数据类型 实现的是深拷贝
半深拷贝 指得是被复制得对象里面有引用数据类型 实现得就是半深拷贝
在例子中,对象的名字属性属于基本能数据类型,实现的是深拷贝,而在对象中的clazz属于引用数据类型,实现的是半深拷贝。
let obj={};
let obj1={
name:'zhangsan',
age:12,
clazz:{
number:"web"
}
}
let res=Object.assign(obj,obj1);
console.log(res,obj);
//{ name: 'zhangsan', age: 12, clazz: { number: 'web' } }
//{ name: 'zhangsan', age: 12, clazz: { number: 'web' } }
console.log(res===obj);//true
obj.name='lisi';
obj.clazz.number='web1';
console.log(obj,obj1);
//{ name: 'lisi', age: 12, clazz: { number: 'web1' } }
//{ name: 'zhangsan', age: 12, clazz: { number: 'web1' } }
三个参数 合并对象 ,返回值:返回第一个参数
let obj={};
let obj1={name:"hangsan",age:12};
let obj2={gender:'male'};
let res=Object.assign(obj,obj1,obj2);
console.log(res,obj);//{ name: 'hangsan', age: 12, gender: 'male' }
console.log(res===obj);//true
- 获取原型对象得方法 getPrototypeOf
let obj={
name:"zhangsan"
};
console.log(obj.__proto__);//[Object: null prototype] {}
console.log(obj.constructor.prototype);//[Object: null prototype] {}
console.log(Object.getPrototypeOf(obj));//[Object: null prototype] {}
- 设置原型对象方法setPrototypeOf
let obj={};//Object.prototype
let obj1={
name:"lisi"
};
Object.setPrototypeOf(obj,obj1);//将obj得原型对象设置成obj1
console.log(obj.__proto__);//{ name: 'lisi' }
console.log(obj.constructor.prototype);//[Object: null prototype] {}
console.log(Object.getPrototypeOf(obj));//{ name: 'lisi' }
- keys values entries
keys 返回键值组成得数组
values 返回属性值组成得数组
entries 返回键值对组成得数组
数组api拓展
- Array.from(); 将类数组转数组
console.log(Array.from('hello'));//[ 'h', 'e', 'l', 'l', 'o' ]
- Array.of(); 创建数组实例
var arr=new Array(10);
var arr1=Array.of(10);
console.log(arr,arr1);//[ <10 empty items> ] [ 10 ]
- Array.prototype.find 查找满足条件数组元素
参数:回调函数(item,index,arr)
返回值:返回第一个符合条件得数组元素或者undefiend
var arr=[1,2,3,4,5];
let res=arr.find((item,index,arr)=>{
return item>2
});
console.log(res);//3
- Array.prototype.findIndex
参数:回调函数 (item,index,arr)
返回值:返回符合条件得第一个数组元素索引或者-1
var arr=[1,2,3,4,5];
let res=arr.findIndex((item,index,arr)=>{
return item>2
});
console.log(res);//2
- keys values entries Array.prototype
返回值:返回一个迭代器对象
var arr=[1,2,3,4,5];
console.log(arr.keys());//Object [Array Iterator] {}
console.log(arr.values());//Object [Array Iterator] {}
console.log(arr.entries());//Object [Array Iterator] {}
- Array.prototype.flat();展平数组
参数不写,默认为1
var arr=[1,2,3,[4,5,[6,7,[8,[9,10]]]]];
console.log(arr.flat(Infinity));//[1,2,3,4,5,[6,7,8]]
类 Class
es5
es5中并没有产生类这个概念,而在es5中如果想要生成一个对象实例,需要定义一个构造函数,然后通过new操作符来完成
function Person(name,age){
this.name = name
this.age = age
}
Person.prototype.sayName = function(){
console.log('my name is ' + this.name);
}
var p1 = new Person('siantop',18)
p1.sayName()
es6中的类
es6中引入了类这个概念,通过class关键字可以定义一个特定的对象,使得js中在对象的写法上更加清晰,更像是一种面向对象编程。
class Person{
// 每个类里面都会有构造方法,用来接收参数,不写也会自动生成
//constructor方法是类的构造函数的默认方法,通过new命令生成对象实例时,自动调用该方法
constructor(name,age){
this.name = name//this指向实例对象
this.age = age
}
// 这是类的方法 不需要加function
say(){
console.log('my name is ' + this.name);
}
}
var p1 = new Person('sintop',19)
p1.say()
注意:
1、类自身指向就是构造函数,可以认为ES6中的类其实就是构造函数的另外一种写法!
2、constructor定义的属性可以称为实例属性(即定义在this对象上),constructor外声明的属性都是定义在原型上的,可以称为原型属性。
3、class不存在变量提升,所以需要先定义再使用。
console.log(Person.prototype);//{}
// 说明构造函数的prototype属性,在es6的类中依然存在
继承
es5的继承
实例使用方法和属性
1、从实例对象本身查找属性或方法
2、如果实例没有,从构造函数的原型对象中找
3、如果还没有,从父构造函数的原型对象中找
原型链继承
父类的实例作为子类的原型
function Person(name,age,type){
this.name = name
this.age = age
this.type = type
this.say = function(){
console.log('my type is ' + this.type + '静态方法');
}
}
Person.prototype.sayName = function(){
console.log('my name is ' + this.name +'原型方法');
}
function student(){}
// 将子类的原型变成构造函数的实例
student.prototype = new Person('sintop',18,'student')
var s1 = new student()
s1.sayName()//my name is sintop原型方法
s1.say()//my type is student静态方法
借用构造函数继承
在子类中,使用call()调用父类的方法,并将this修改为子类的this,相当于把父类的实例属性复制了一份放到子类中。
function Person(name,age){
this.name = name;
this.age = age;
this.sayHi = function (){
console.log('hi! '+ this.name);
}
}
Person.prototype.sayName = function(){
console.log('my name is ' + this.name);
}
// 定义子类
function student(name,age){
Person.call(this,name,age)
}
var s = new student('sintop',19)
var s1 = new student('lays',10)
// s.sayName()//报错,不能继承原型上的方法
s.sayHi();//hi! sintop
组合继承(借用构造函数+原型链继承)
既能调用父类实例属性,又能调用父类原型属性
function Animal(type,age,weight,length){
this.type = type
this.age = age
this.weight = weight
this.length = length
}
Animal.prototype = {
constructor:Animal,
sayType:function(){
console.log(this.type);
}
}
function Dog(type,age,weight,length,name,color){
// 经典继承又称为构造函数继承
Animal.call(this.type,age,weight,length)
this.name = name
this.color = color
}
Dog.prototype = new Animal()
Dog.prototype.costructor = Dog
Dog.prototype.sayColor = function(){
console.log(this.color);
}
var d1 = new Dog('狗',1,'10kg','40cm','可乐','白色')
console.log(d1);
d1.sayType()
d1.sayColor()
Es6中的类继承
1、class 相当于es5中的构造函数
2、class中定义方法时,前后不能加function,全部定义在class的prototype属性中
3、class中定义的所有方法是不可枚举的
4、class中只能定义方法,不能定义对象,变量等
5、class和方法内默认都是严格模式
es5中的constructor为隐式属性
class Animal{
// 静态属性
static animalAttr = 'Animal的静态属性'
constructor(name,age,weight){
this.name = name;
this.age = age;
this.weight = weight;
}
// 实例方法
sayName(){
console.log('实例方法');
}
// 静态方法
static animalMethod(){
console.log('Animal静态方法');
}
}
// 要实现继承
class Dog extends Animal {
constructor(name,age,weight,type){
super(name,age,weight)
this.type = type
console.log('Dog的构造器');
}
}
let dog = new Dog('豆豆',11,30,'dog')
dog.sayName()
symbol
es6引入的一种新的原始数据类型symbol,表示独一无二的值。symbol函数可以接收参数,表示对于这个唯一值的描述,属于基本数据类型,symbol()函数会返回symbo类型的值。
- 创建一个symbol值
const a = Symbol()
console.log(a);//Symbol
//或者,你也可以在调用Symbol()函数时传入一个可选的字符串参数,相当于给你创建的Symbol实例一个描述信息:
let s2 = Symbol('another symbol')
//使用typeof判断symbol值的类型为symbol
console.log( typeof a);//Symbol
//判断两个类型为symbol,值为一样的,返回的结果会是false
const a=Symbol("name");
const b=Symbol("name");
console.log(a===b) //false
symbol的使用场景
- 使用symbol作为对象属性名
const name=Symbol();
const age=Symbol();
let obj={
[name]:"已经代码"
}
obj[age]=18;
console.log(obj[name]);//已经代码
console.log(obj[age]);//18
*注意:*当使用了symbol作为对象的属性key后不能使用枚举
const name=Symbol();
const age=Symbol();
const obj={
[name]:"大师",
[age]:16,
sex:"男"
}
console.log(obj)
console.log(Object.keys(obj));//["sex"] 使用symbol定义的属性,不能使用枚举遍历
for(let p in obj){
console.log(p); // sex
}
// 使用Object的API
console.log(Object.getOwnPropertyNames(obj));// ["sex"] 得到符号类型代替的属性名,并且为数组
console.log(Object.getOwnPropertySymbols(obj));// [Symbol(), Symbol()]
const sybs=Object.getOwnPropertySymbols(obj);
console.log(sybs[0]===name) //true 引用会相等
//补充:
1.Object.getOwnPropertyNames()方法返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性但不包括Symbol值作为名称的属性)组成的数组。
2.Object.getOwnPropertySymbols()方法返回一个给定对象自身的所有 Symbol 属性的数组。
- 使用symbol定义类的私有属性/方法
在javaScript中,没有如同java等面向对象语言的定义私有属性的private方法,类上所有定义的属性或者方法都是可以公开访问的。有了Symbol以及模块化机制,类的私有属性和方法变得可能。
a.js
const PASSWORD = Symbol()
class Login {
constructor(username,password){
this.username = username;
this[PASSWORD] = password
}
checkPassword(pwd){
return this[PASSWORD] === pwd
}
}
export default Login;
b.js
import Login from "./a";
const login = new Login("admin", "123456");
login.checkPassword("123456"); // true
login.PASSWORD; // undefined
login[PASSWORD]; // undefined
login["PASSWORD"]; // undefined
由于Symbol常量PASSWORD被定义在a.js模块中,外面模块b.js无法获取此Symbol(Symbol是唯一的),因此PASSWORD只能被限定在a.js中使用,使用它来定义类属性外部模块无法访问,达到了私有化的目的。