美文网首页
JS 内置类继承和类的混入、字面量增强、解构、let-const

JS 内置类继承和类的混入、字面量增强、解构、let-const

作者: 咸鱼不咸_123 | 来源:发表于2022-05-03 15:14 被阅读0次

1.类的相关用法

1.1 继承内置类

class Person{ //* Person默认是继承Object类

}

class HyArray extends Array{
  firstItem(){
    return this[0];
  }
  lastItem(){
    return this[this.length-1]
  }

}
var arr=new HyArray(1,2,3,4);
console.log(arr);
console.log(arr.firstItem());
console.log(arr.lastItem());

1.2 类的混入mixin

JS只能允许有一个直接的父类,不能继承多个类。

混入的本质是继承。

混入的实现的作用:继承多个类,然后复用这些类的一些方法和属性

// * JS只支持单继承 ,只能有一个父类
class Person{

}
function mixinRunner(baseClass){
  class newClass extends baseClass{
    running(){
      console.log("跑步");
    }
  }
  return newClass;
}

function mixinEater(baseClass){
  class newClass extends baseClass{
    eating(){
      console.log("吃东西");
    }
  }
  return newClass
}


class Student extends Person{

}

var newClass=mixinEater(mixinRunner(Student));
var p=new newClass()
p.running();
p.eating()

react开发更加接近于原生js开发,更加灵活。

react(双刃剑):①对开发者要求比较高 ② 更加容易出错 ③维护起来比较困难。

react的redux(状态管理工具):相当于vue的vuex

2.JavaScript的多态

面向对象的三大特性:封装、继承、多态

抽象:将现实世界的事物抽象为代码中的某个数据结构。

2.1 维基百科的解释

多态(polymorphism)指为不同数据类型的实体提供统一的接口,或使用一个单一的符号来表示多个不同的类型。

个人总结:不同的数据类型进行同一个操作,表现出不同的行为,就是多态的体现。

2.1.1 传统面向对象多态的特点

传统面向对象实现多态的三个特点:

  • 必须使用有子类继承父类的方法
  • 必须重写父类的方法
  • 必须有父类引用子类对象
// * 传统面向对象的三个必要条件
// * 1.继承是实现多态的前提
// * 2. 必须有重写
// * 3. 必须有父类引用子类对象
class Shape{
  getArea(){
    console.log("获取面积");
    
  }
}

class Rectangle extends Shape{
  getArea(){
      return 100;
  }
  
}

class Circle extends Shape{
  getArea() {
      return 1;
  }
}

var r=new Rectangle();
var c=new Circle();
function calcArea(shape:Shape){
  shape.getArea()

}
console.log(calcArea(r));

console.log(calcArea(c));
2.1.2 javascript面向对象多态的特点
function calcArea(foo){
  console.log(foo.getArea());
}

var obj1={
  name:"wjy",
  getArea(){
    return 100;
  }
}

class Person{
  getArea(){
    return 1000
  }
}
var p=new Person();
calcArea(p);
calcArea(obj1);
// * 不同数据类型的对象执行同一个操作,会呈现不同的形式。
function sum(m,n){ 
  return m+n;
}
// * 这也是不同数据类型的数据,执行同一个操作,会呈现不同的表现形式。
console.log(sum(1,2));
console.log(sum("hello ","world"));

3.字面量的增强

3.1 对象字面量和块级作用域的区别

var obj={};//{}为对象字面量
{  //这个是块级作用域

}

3.2 属性简写 (property shorthand)

es5之前对对象属性的定义是这样的:

var name="wjy";
var age=20;
var obj={
  name:name,
  age:age
}

es6:如果属性和变量名一致,可以缩写。

var name="wjy";
var age=20;
var obj={
  name,
  age
}

3.3 方法简写 (method shorthand)

es5之前:

// * 对象字面量增强
var obj={
  foo:function(){
    
  }
}

es6之后:

var obj={
  foo(){
    
  }
}
45.png

3.4 计算属性名(Computed Property Names)

es5之前定义的计算属性:属性名是通过计算出来的

var name="wjy"
var obj={};
obj[name+123]="123"

es6之前可以定义这样定义计算属性:

var name="wjy";
var obj={
    [name+123]:"123"
}

4.解构Destructuring

ES6中新增了一个从数组或对象中方便获取数据的方法,称之为 解构Destructuring。

4.1 数组的解构

数组是按顺序解构的

  • 数组中的元素全部解构
  • 解构后面的参数,可以通过英文逗号跳过
  • 解构出数组
  • 解构的默认值
var names=["abc","efg","ddd"]
// 对数组的解构
var [item1,item2,item3]=names;
console.log(item1,item2,item3);//abc efg ddd

将es6转化为es5的代码:

"use strict";

var names = ["abc", "efg", "ddd"]; // 对数组的解构

var item1 = names[0],
  item2 = names[1],
  item3 = names[2];
console.log(item1, item2, item3);

示例代码:

var names=["abc","efg","ddd"]

// * 对数组的解构
var [item1,item2,item3]=names;
console.log(item1,item2,item3);//abc efg ddd

// * 解构后面的元素
var [,itemA,itemB]=names;
console.log(itemA,itemB);//efg ddd

// * 解构出一个元素,后面的元素放到一个新数组中,用到了剩余参数,
var [itema,...newNames]=names;
console.log(itema,newNames);//abc [ 'efg', 'ddd' ]


// * 解构的默认值
var [a,b,c,d='aaa']=names;
console.log(a,b,c,d); //abc efg ddd aaa

4.2 对象的解构

对象的解构是一个大括号。

对象的解构是按照key来解构的。

它默认是根据变量名去取对象的某个属性名也是这个变量名,如果变量名在对象的属性中找不到,这个变量最后的值是undefined

  • 基本解构过程
  • 任意顺序
  • 重命名
  • 默认值
4.2.1 全部解构
var obj={
  name:"wjy",
  age:20,
  height:1.88
}
// * 对象的解构用的是大括号,不必按照顺序解构
var {name,age,height}=obj;
console.log(name,age,height);//wjy 20 1.88
4.2.2 部分解构
// * 也可以部分解构
var obj={
  name:"wjy",
  age:20,
  height:1.88
};
var {age}=obj;
4.2.3 给新的变量名赋值
var obj={
  name:"wjy",
  age:20,
  height:1.88
};
// * 从这个对象找到属性为name,并将值赋给一个新的变量(这里是newName)
var {name:newName}=obj;
console.log(newName);//wjy
4.2.4 默认值

当要提取的对象没有对应的属性,变量就被赋予默认值。

var obj={
  name:"wjy",
  age:20,
  height:1.88
};
// * 默认值
var {address="怀化"}=obj;
console.log(address);//怀化

4.3 应用场景

  • 比如在开发中拿到一个变量时,自动对其进行解构使用。
  • 比如对函数的参数解构
var obj={
  name:"wjy",
  age:20,
  height:1.88
}
// * 应用场景
function foo(info){
  console.log(info.name,info.age);
}

foo(obj);
// * 函数的参数解构
function bar({name,age}){
  console.log(name,age);
}
bar(obj)

5.let/const

在ES5中我们只能通过var关键字来声明变量,从ES6中新增了两个关键字可以声明变量:letconst

  • let、const在其他编程语言中都是有的,所以并不是新鲜的关键字
  • 但是let、const确确实实给javascript带来了不一样的东西

let关键字

  • 从直观的角度来说,let和var并没有太大的区别的,都是用于声明一个变量

const关键字

  • const关键字是constant单词的缩写:表示常量、衡量的意思
  • 它表示保存的数据一旦被赋值,就不能被修改。
  • 但是如果赋值的是一个引用类型(内存地址),则可以通过引用找到对象,修改对象内部的值。

5.1 const和let的基本使用

  • 注意一:const本质上是值不能修改

    • 如果const定义的变量的值是引用类型(内存地址),则可以通过引用找到对象,修改对象内部的属性
  • 注意二:let和const不能重复定义变量,但var可以

  • 注意三:const在定义变量的同时,必须赋值。

 var foo="foo";
 let bar="bar";
 
//  * const是声明的一个常量
// variable declaration:变量声明

const name="abc";
// name="ddd";//报错
// * 注意事项一:
// * const本质上是传递的值不可以修改,
// * 但是如果传递的是引用类型(内存地址):可以通过引用找到对应的对象,修改对象内部的属性

const obj={
  foo:"foo"
};
obj.foo="wjy"

// obj={};//报错

// * 注意事项二:var可以重复定义变量,let和const不可以重复定义变量
var foo="abc";
var foo="dd";
console.log(foo);//dd
let a="a";
// let a="b";// Identifier 'a' has already been declared

// const c;//报错

5.2 let/const的作用域提升

let、const和var的另一个重要区别是作用域提升:

  • 我们知道var声明的变量是会进行作用域提升的。
  • 但是如果我们使用let声明的变量,在声明访问之前会报错

那么是不是意味着foo变量只有在代码执行阶段才会被创建呢?

  • 事实上并不是这样的,我们可以看一下ECMA262对let和const的描述
  • 这些变量会被创建在包含他们的词法环境被实例化时,但是是不可以访问它们的,直到词法绑定被求值时

5.3 let/const 有没有作用域提升呢?

从上面我们可以看出,在执行上下文的词法环境创建出来的时候,变量事实上已经被创建了,只是这个变量是不能被访问的。

  • 那么变量已经有了,但是不能被访问,是不是一种作用域的提升呢?

事实上维基百科并没有对作用域提升有严格的概念解释,那么我们自己从字面上理解

  • 作用域提升:在声明变量的作用域中,如果这个变量可以在声明之前被访问,那么我们可以称之为作用域提升
  • 在这里,它虽然被创建出来了,但是不能被访问,我认为是不能称之为作用域提升。

所以我的观点是let、const没有作用域提升,但是会在执行上下文创建阶段被创建出来

5.4 window对象添加属性

我们知道,在全局通过var声明一个变量,事实上会在window添加一个属性

但是let、const是不会给window添加任何属性的。

那么这个变量是保存在哪里呢?

我们看一下ECMA标准对执行上下文的描述

  • 早期:每个执行上下文会被关联到一个变量对象中(Variable Object),源代码中的变量和函数声明会作为属性添加到VO对象中。对函数而言,参数也会加入到VO中
  • 后期:每个执行上下文会被关联到一个变量环境中(Variable Environment),执行代码中的变量和函数声明会作为环境记录,添加到变量环境。对函数而言,参数也会添加到VE中

5.5 变量被保存到VariableMap中

也就是说 我们声明的变量和环境记录是被添加到变量环境中的:

但是标准有没有规定这个对象是window对象还是其他对象?

其实并没有,每个JS引擎在解析的时候,其实会有自己的实现。

比如V8中是通过VariableMap的一个hashmap来实现它们的存储的。

那么window对象呢?而window对象是早期的GO对象,在最新的实现中其实是浏览器添加的全局对象,并且一直保持了window和var之间值的相等性。

V8中:
VE指向的是一个变量名为variable_:类型是VariableMap

5.6 块级作用域

5.6.1 块级作用域对var不生效

在ES5中,单独写一个{}代表是一个代码块.

ES5中只有两个可以形成作用域:

  • 函数作用域
  • 全局作用域
{
    var a="aaa"
}
console.log(a);//还是可以访问到的,因为块级作用域对var无效
5.6.2 ES6的块级作用域

在ES6中新增了一个块级作用域,它对var无效,但对let、const、function、class声明的标识符是具有块级作用域的限制的。

{
    let a='aaa';
    function demo(){
        
    }
    class Person{
        
    }
}
console.log(a);//报错
console.log(demo);//不报错
console.log(Person);//报错

但上面的代码使用function定义的函数为什么不会报错呢?

因为在不同的浏览器有不同的实现,有些浏览器为了兼容,让函数没有块级作用域。

如果某个浏览器仅支持ES6的语法,那么上面的function定义的函数一定会报错的。

5.6.2.1 if-switch-for有块级作用域
// * 代码块:也会形成块级作用域
{

}
// * if也会形成块级作用域
// if(true){
//    let foo="foo";
// }
// console.log(foo);//* ReferenceError: bar is not defined

// * switch:块级作用域
// var color="red";
// switch(color){
//   case "red":var foo="foo";let bar="bar"
// } 
// console.log(foo);//foo
// console.log(bar);//* ReferenceError: bar is not defined

// * for:块级作用域
// * 使用var 声明的变量:在外层是可以访问到的
// for(var i=0;i<10;i++){
//   // console.log(i);
// }
// console.log(i);// 10

// * 使用let声明的变量:在外层是访问不到的
for(let i=0;i<10;i++){
  console.log(i);
}
console.log(i);//* ReferenceError: i is not defined

5.6.3 应用场景

例如:给10个按钮,添加点击事件,点击第几个按钮,就输出 第几个按钮被点击了。

const btns=document.getElementsByTagName("button");
for(var i=0;i<btns.length;i++){
  btns[i].onclick=function(){
    console.log("第"+i+"个按钮被点击了。");
  }
}

上面这段代码有问题:就是无论点击第几个按钮,最后都输出 的是 :第10个按钮被点击了。

这是为什么呢?因为对应的按钮被点击的时候会调用对应的处理函数,要取i的值的时候,去上层作用域(块级作用域)找,发现没有,又去全局作用域中,发现i的值是10,因为在进行遍历for循环的时候,,终止的时候i的值就已经是10了。

5.6.3.1 解决方案一:立即执行函数

立即执行函数形成自己的函数作用域,在遍历的时候,将每个i传进行,最后会打印出不同的值,其实这个本质上是形成了闭包。

const btns=document.getElementsByTagName("button");
// * 解决方案一:立即执行函数会形成自己的作用域
for(var i=0;i<btns.length;i++){
  (function(n){ 
    btns[i].onclick=function(){
      console.log("第"+i+"按钮被点击了。");
    }
  })(i)
}
5.6.3.2 解决方案二:let

let定义的变量在自己的块级作用域有限制、

const btns=document.getElementsByTagName("button");
// * 解决方案二:使用let:其实在变量每次遍历时,会产生一个新的块级作用域,所以在不同块级作用域中,i的是不一样的,所以在函数内部能打印不同i的值
for(let i=0;i<btns.length;i++){
  btns[i].onclick=function(){
    console.log("第"+(i+1)+"个按钮被点击了。");
  }
}

6.总结

相关文章

  • JS 内置类继承和类的混入、字面量增强、解构、let-const

    1.类的相关用法 1.1 继承内置类 1.2 类的混入mixin JS只能允许有一个直接的父类,不能继承多个类。 ...

  • ES6特性

    箭头操作符 类的支持 增强的对象字面量 字符串模板 解构 参数默认,不定参数, 拓展参数 默认参数值 不定参数 拓...

  • js中的(混合对象)类

    显式混入 js手动实现类的复制功能 寄生继承 以上例子,现复制父类Vehicle的定义,然后再混入子类的定义(如果...

  • 《Java编程思想 Generics》读书笔记——Mixin

    何谓Mixin Mixin即mix in,混入的意思。和多重继承类似,但通常混入Mixin的类和Mixin类本身不...

  • 17.继承内置类和js中的多态

    1.继承内置类 定义类的时候,如果没有实现继承,默认继承自Object类 相当于下面 3.js中的多态 面向对象的...

  • ES6(1)

    let & const 箭头操作符 class类的支持 增强的对象字面量对象字面量被增强了,写法更加简洁与灵活,同...

  • ES6~ES8 新功能

    ES6: 1. 箭头函数 2. 类class 3. 增强的对象字面量 4. 模板字符串 5. 解构 6. 默认值+...

  • JS对象和继承

    JS 对象创建的三种方式 字面量创建方式 系统内置构造函数方式 自定义构造函数 继承方式 for in 继承 原型...

  • ES2015学习笔记(1)——简单概念

    和ES5有对应关系的几个增强特性,包括:变量和常量;解构赋值;for...of迭代;增强对象字面量;箭头函数;参数...

  • ES6--(5)解构:更方便的数据访问

    解构为何有用? 解构使得在 JS 中操作对象与数组变得更容易。使用熟悉的对象字面量与数组字面量语法,可以将数据结构...

网友评论

      本文标题:JS 内置类继承和类的混入、字面量增强、解构、let-const

      本文链接:https://www.haomeiwen.com/subject/bvzbyrtx.html