美文网首页
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

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