对象
新增的对象字面量语法
- 成员速写
如果对象字面量初始化时,成员的名称来自一个变量,并且和变量名称相同,则可以进行简写
function obj (name,age){
let sayHello = function(){
console.log(this.name,this.age);
}
return {
naem,//成员名称与变量相同可简写
age,
sayHello,//函数也一样
}
}
obj("java","script");
- 方法速写
对象字面初始化时,方法可以省略冒号和function关键字
let obj = {
name:"java",
age:"script",
//下面写法与 fn:function(){ console.log(this.name,this.age);}无区别,只是更方便
fn(){
console.log(this.name,this.age);
}
}
- 计算属性名
有的时候,初始化对象时,某些属性名可能来自于某个表达式的值,在es6,可以使用中括号来表示该属性名是通过计算得到的
let a = "name";
let b = "age";
let c ="sayHello";
let obj = {};
//想要上面的变量的值,表示对象obj中的属性名,es6前做法
obj[a]="java";
obj[b]= "script"
obj[c]=function(){};
//es6:可以直接在对象中用[]来表示
obj= {
[a]:"java",
[b]:"script",
[c]:function(){}
}
Object的新增API
- Object.is
用来判断两个数据是否相等,基本上跟严格相等(===)是一致的,除了以下两个点:
console.log(NaN===NaN);//false 明明两个一样的却不相等
console.log(+0===-0);//true +0,-0计算机存储的二进制不同,却相等
//Object.is就是为了解决这两个问题新增的
console.log(Object.is(NaN,NaN));//true
console.log(Object.is(+0,-0));//fales
- Object.assign
用过合并对象
let obj1 ={a:1,b:2};
let obj2 ={a:2,b:2,c:4};
//相同于浅克隆
let obj3= Object.assign(obj1,obj2);//obj2合并到obj1中,然后返回obj1
console.log(obj1 == obj3);//true
//另外还可以用es7的...
let obj4 ={
...obj1,
...obj2
}
- Object.getOwnPropertyNames的枚举顺序
Objec.getOwnPropertyNames方法之前就存在,只不过,官方没有明确要求,对属性的顺序如何排序,完全由浏览器厂商决定
es6规定了该方法返回的数组的排列方式如下:
- 先排数字,并按照升序顺序
- 再排其他
- Object.setPrototypeOf
该函数用于设置某个对象的隐式原型
let a={a:1};
let b ={b:2};
//obj1.__proto__=obj2
Object.setPrototypeOf(obj1,obj2);
类:构造函数
传统的构造函数的问题
- 属性和原型方法定义分离,降低了可读性
function Person(name,age){
this.name = name;
this.age = age;
}
//如果代码量多,总是XX.prototype.XX下去,会造成不易阅读
Person.prototype.show = function(){}
- 原型成员可以被枚举
function Person(name,age){
this.name = name;
this.age = age;
}
//如果代码量多,总是XX.prototype.XX下去,会造成不易阅读
Person.prototype.show = function(){}
let pr = new Person();
for(let prop in pr ){
console.log(prop);
}
- 默认情况下,构造函数仍然可以被当作普通函数使用
类的特点
- 类声明不会被提升,与let和const一样,存在暂时性死区
- 类中的所有代码均在严格模式执行
- 类的所有方法都是不可枚举的
- 类的所有方法都是无法被当作构造函数使用
- 类的构造器必须使用new来调用
class Person {
constructer(name,age){//相当于es5中的构造函数
this.name = name;
this.age = age;
}
show(){
}
}
let pr = new Person("java","script");
let sh =new pr.show(); //类的所有方法都是无法被当作构造函数使用
for(let prop in person){
console.log(prop);//不会遍历原型上的方法
}
console.log(wid)
类的其他书写方式
- 可计算的成员名
let methedName = "show";
class Person {
constructer(name,age){
this.name = name;
this.age = age;
}
[methedName](){
}
}
- getter和setter
es5中Object.defineProperty可定义某个对象成员属性的读取与设置
class Person {
constructer(name,age){
this.name = name;
this.age = age;//直接赋值就可以,下面方法会运行
//如果作es5的Object.defineProperty
//参数分别为对象,属性名,配制属性
Object.defineProperty(this,"age",{
set(val){},
get(){}
})
}
//创建一个age属性,并给它加上getter,读取该属性时,会运行该函数
get age (){//没有参数
return this.age +"岁";
}
//创建一个age属性时,并给它加上setter,给该属性赋值时,会运行该函数
set age(age){//参数
if(age<0){
age = 0;
}
else if(age>1000){
age = 1000;
}
}
show(){
}
}
- 静态成员
静态成员是在构造函数本身上的成员
使用static关键字定义的成员即静态成员
每次new创建对象时都会占内存
class Person {
constructer(name,age){
this.name = name;
this.age = age;
}
static sex = "男";
static weight = 120;
static mehtod(){}
show(){
}
}
console.dir(Person);
//取静态成员
Person.medthod();
Person.sex
person.weight;
- 字段初始化器(es7)
class Text(){
a = 1;//相同于在constructer构造函数中的this.a
constructer(){
//this.a = 1;//实例成员是固定值,可以换一种写法,直接在constructer外写
}
}
注意:
- 使用static的字段初始化器,添加的是静态成员
- 没有使用static的字段初始化器,添加的成员位于实例对象上
- 箭头函数在字段初始化器位置上,指向当前对象
class Text(){
method = ()=>{};
//上面这种写法相同于
constructer(){
this.method = ()=>{}//this指向的是Text对象
}
}
- 类表达式
装饰器的本质是一个函数
const obj = class {//匿名类
a = 1;
b = 2;
}
- [扩展]装饰器(es7,暂没有成为正式标准)
class Text{
@Obsolete
show(){
console.log("show已过时");
}
}
//看不到效果
funtion Obsolete (target,methodName,descriptor){//对象,方法名,描述
console.log(target,methodName,descriptor);
}
类的继承
如果两个类A和B,如果可以描述为:B是A,则A和B形成继承关系
如果B是A,则:
- B继承A
- A派生B
- B是A的子类
- A是B的父类
如果A是B的父类,则B会自动拥有A中的所有实例成员
//过去继承写法
function Animal(type,name,age,sex){
this.type = type;
this.name = name;
this.age = age;
this.sex = sex;
}
Animal.prototype.print = function(){
console.log(this.type,this.name,this.age,this.sex);
}
function Dog(name,age,sex){
//借用父类的构造函数
Animal.call(this,"狗",name,age,sex);
}
Object.setPrototypeOf(Dog.prototype,Animal.prototype);//将Dog的隐式原型设置成Animal
let dog = new Dog("旺财",3,"male");
//es6继承写法
class Animal{
construtor(type,name,age,sex){
this.type = type;
this.name = name;
this.age = age;
this.sex = sex;
}
}
class Dog extends Animal{
construtor(name ,age,sex){
//调用父类的构造函数
super("狗",name,age,sex);//或者Animal.call(this,"狗",name,age,sex);
}
}
新的关键字:
extends
:继承,用于类的定义super
直接当作函数调用,表示父类构造函数
注意:es6中规定如果类是子类必须在construtor中第一行手动调用父类的构造函数
如果子类不写contrutor,则会有默认的构造器,该构造器需要参数和父类一致,并且自动调用父类构造器
//子类不定construtor构造函数的默认情况
class Animal{
construtor(type,name,age,sex){
this.type = type;
this.name = name;
this.age = age;
this.sex = sex;
}
}
class Dog extends Animal{
//默认构造器
//这种情况下,如果子类构造函数参数与父类不同,则会出现参数错乱的情况
construtor(type,name,age,sex){
super(type,name,age,sex);
}
}
【冷知识】
- 用js制作抽象类
- 抽象类:一般是父类,不能通过该类创建对象
class Animal{
construtor(type,name,age,sex){
this.type = type;
this.name = name;
this.age = age;
this.sex = sex;
}
}
class Dog extends Animal{
construtor(name ,age,sex){
super("狗",name,age,sex);
}
}
let dog = new Animal("狗","旺财",3,"male");//逻辑不正确,不应该用Animal类创建一个Dog类,Animal类是抽象的
//解决方法
class Animal{
construtor(type,name,age,sex){
if(new.target === Animal){//利用new.target
throw new Error("你不能直接创建Animal的对象,应该通过子类创建");
}
this.type = type;
this.name = name;
this.age = age;
this.sex = sex;
}
}
class Dog extends Animal{
construtor(name ,age,sex){
super("狗",name,age,sex);
}
}
let dog = new Dog("旺财",3,"male");
let dog = new Animal("狗","旺财",3,"male");//出错
- 正常情况下,this始终指向具体的类的对象