JavaScript系列之高级篇

Posted coderkey

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaScript系列之高级篇相关的知识,希望对你有一定的参考价值。

一,面向对象♥

1,面向对象的编程介绍

1.1,两大编程思想

(1)含义:

面向过程和面向对象

1.2,面向过程编程 POP(Process-oriented Programming)

(1)含义:

面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候再一个一个的依次调用就可以了。

1.3,面向对象编程 OOP(Object Oriented Programming)

(1)含义:

面向对象是把事务分解成为一个一个对象,然后由对象之间分工与合作。面向对象是以对象功能来划分问题,而不是步骤。

(2)特点:

封装性、继承性和多态性。

2,ES6 中的类和对象

2.1,对象

(1)含义:

javascript中,对象是一组无序的相关属性和方法的集合,对象是具体的事物,所有的事物都是对象,例如字符串、数值、数组、函数等。

(2)组成:

对象是由属性和方法组成的。

2.2,类 class

(1)含义:

在ES6 中新增加了类的概念,可以使用 class 关键字声明一个类,之后以这个类来实例化对象。
类抽象了对象的公共部分,它泛指某一大类(class)。
对象特指某一个,通过类实例化一个具体的对象。

(2)特点:

1,抽取(抽象)对象共用的属性和行为组织(封装)成一个类(模板)
2,对类进行实例化,获取类的对象。

2.3,创建类

(1)语法:

class name {
// class body
}

(2)创建实例:

  var  变量名 = new name( );

(3)注意:

类必须使用 new 实例化对象。

(4)例如:

  // 1,创建类 class 
     class  Star {
     //代码
    }
   // 2,利用类创建对象 new
   new  Star( );

2.4,类 constructor 构造函数

(1)含义:

constructor( ) 方法是类的构造函数(默认方法),用于传递参数,返回实例对象,通过 new命令生成对象实例时,自动调用该方法。如果没有显示定义,类内部会自动给我们创建一个constructor( )

(2)例如:

   // 1,创建类 class 
     class  Star {
                constructor(uname, age) {
                     this.name = uname;
                     this.age = age;
               }
       }
   // 2,利用类创建对象 new
   var  ldh =  new  Star('刘德华', 20 );
   var  zxy =  new  Star('张学友', 18);
   console.log(ldh.name);    //  刘德华    
   console.log(zxy.age);    //  18  

(3)注意:

1,通过 class 关键字创建类,类名我们还是习惯性定义首字母大写。
2,类里面有个 constructor 函数,可以接受传递过来的参数,同时返回实例对象。
3,constructor 函数,只要 new生成实例时,就会自动调用这个函数,如果不写这个函数,类也 会自动生成这个函数。
4,生成实例 new 不能省略。
5,创建类,类名后面不加小括号。生成实例,类名后面加小括号,构造函数不需要加 function

2.5,类中添加共有方法

(1)例如:

  <script>
       // 1,创建类 class
        class Star {
                   constructor(uname, age) {
                        this.name = uname;
                        this.age = age;
                  }
                  sing(song) {
                    console.log(this.name + song);
                  }
          }
      // 2,利用类创建对象 new
      var ldh =  new  Star('刘德华', 20 );
      var zxy =  new  Star('张学友', 18);
      console.log(ldh.name);    //  刘德华
      console.log(zxy.age);    //  18
      ldh.sing('冰雨');
    </script>

(2)注意:

1,类里面所有的函数不需要写function
2,多个函数方法之间不需要添加逗号分隔。

3,类的继承

3.1,类继承 extends

(1)含义:

子类可以继承父类的一些属性和方法。

(2)语法:

class Father {
// 父类
}
class Son extends Father {
// 子类继承父类
}

(3)例如:

 <script>
      class Father {
        constructor( ) { }
        money( ) {
          console.log(100);
        }
      }
      class Son extends Father { }
      var son = new Son( );
      son.money( );
  </script>

3.2,super 关键字

(1)含义:

super 关键字用于访问和调用对象父类上的函数。可以调用父类的构造函数,也可以调用父类的普通函数。

(2)例如:

 <script>
      class Father {
        constructor(x, y) {
          this.x = x;
          this.y = y;
        }
     // 父类中的this指向的是父类构造函数创建的实例对象
     // 因为父类中的函数所访问的变量是在父类的实例对象身上去找的,
     // 所以子类若想在调用的父类的方法中使用自身的变量,则需要通过super调用父类构造函数将
    其值传入父类中。
        sum( ) {
          console.log(this.x + this.y);
        }
      }
      // 关键字extends
      class Son extends Father {
        constructor(x, y) {
          // 通过super调用父类的构造函数
          super(x, y);
        }
      }
      var son = new Son(1, 2);
      var son2 = new Son(2, 3);
      son.sum( );    // 3
      son2.sum( );    // 4
      // 可能出现的报错:
      // ReferenceError: Must call super constructor in derived class before accessing 'this'
      or returning from derived constructor
      // 翻译: 当在访问this或在子构造函数return之前, 必须在子构造函数中先调用super构造函
        数。
      // 解释:在子类访问this或在子类的构造函数返回实例对象前,必须调用super父类构造函数
     </script>

    <script>
        // super 关键字调用父类普通函数
        class Father {
            constructor( ) { }
         say( ) {
           // console.log('this is father'); 
           return "this is father";
         }
       }
       class Son extends Father {
        constructor( ) {
          super( );
        }
        say( ) {
          console.log(super.say( ) + " this is son"); //在console代码中打印某函数的结果时,应该
      采用return的方式返回数据,而不应该在原函数中打印,这是错误的编程方式
        }
      }
      var son = new Son();
      son.say();
      // 若父类和子类含有相同的函数,则会应用就近原则,优先调用子类的函数。
    </script>

   <script>
      class Father {
        constructor(x, y) {
            this.x = x
            this.y = y
        }
        sum() {
            console.log(this.x + this.y);
        }
    }
    class Son extends Father {
        constructor(x, y) {
            super(x, y);  // super 必须在子类 this 之前调用
            this.x = x   // 这两句可以省略
            this.y = y
        }
        subtract() {
            console.log(this.x - this.y);
        }
    }
    var son = new Son(4, 5)
    son.subtract();   //-1
    son.sum();    //9
    </script>

(3)注意:

1,子类在构造函数中使用super,必须放到 this 前面(必须先调用父类构造方法,再使用子类构造方法)
2,es6中类没有变量提升,必须先定义类,再创建实例对象。
3,类中的共有属性和方法在访问时必须加 this


二,对象高级

1,对象的创建模式

1.1,Object构造函数模式

(1)用法:

方式一:Object构造函数模式
套路:先创建空Object对象,再动态添加属性/方法
使用场景:起始时不确定对象内部数据
问题:代码量多

(2)例如:

<script>
        var p = new Object();
        p.name = "tom";
        p.age = 12;
        p.setName = function(name){
            this.name = name;
        };
 
        // 测试
        p.setName("李白");
        console.log(p.name,p.age);
    </script>

1.2,对象字面量模式
(1)用法:

方式二:对象字面量模式
套路:使用{}创建对象,同时指定属性和方法
适用场景:起始时对象内部数据时确定的
问题:如果创建多个对象,有重复代码

(2)例如:

<script>
        var p = {
            name:"tom",
            age:16,
            setName:function(name){
                this.name = name;
            }
        };

        // 测试
        console.log(p.name,p.age);
        p.setName("Jack");
        console.log(p.name,p.age);
    </script>

1.3,工厂模式

(1)用法:

方式三:工厂模式
套路:通过工厂函数动态创建对象并返回
适用场景:需要创建多个对象
问题:对象没有一个具体的类型,都是Object类型

(2)例如:

<script>
        function createPerson(age,name){    //返回一个对象的函数-->工厂函数  
            var obj = {
                age:age,
                name:name,
                setName:function(){
                    this.name = name;
                }
            };
            return obj;
        }

         var p1 = createPerson(16,"张三");
         var p2 = createPerson(16,"李三");

        console.log(p1);
        console.log(p2);

        function createCat(age,name){
            var obj = {
                age:age,
                name:name,
                setName:function(){
                    this.name = name;
                }
            };
            return obj;
        }

        var c1 = createCat(15,"波斯猫")
        var c2 = createCat(15,"野猫")

        console.log(c1);
        console.log(c2);
        
    </script>

1.4,自定义构造函数模式

(1)用法:

方式四:自定义构造函数模式
套路:自定义构造函数,通过new创建对象
适用场景:需要创建多个类型确定的对象
问题:每个对象都有相同的数据,浪费内存

(2)例如:

<script>
        // 定义类型
        function Person(name,age){
            this.name = name;
            this.age = age;
            this.setName = function(){
                this.name = name;
            };
        }

        var p1 = new Person("lisa",16);
        p1.setName("bob");
        console.log(p1.name,p1.age);
        console.log(p1 instanceof Person);

        function Student(name,price){
            this.name = name;
            this.price = price;
            this.setName = function(){
                this.name = name;
            };
        }

        var s = new Student("熊大",1000);
        console.log(s instanceof Student);

        var p2 = new Person("Jack",16);
        console.log(p1,p2)
    </script>

1.5,构造函数+原型组合模式

(1)用法:

方式五:构造函数+原型组合
套路:自定义构造函数,属性在函数中初始化,方法添加到原型上
适用场景:需要创建多个类型确定的对象

(2)例如:

<script>
        function Person(name,age){
            this.name = name;
            this.age = age;
        }
        Person.prototype.setName = function(name){
            this.name = name;
        };

        var p1 = new Person("tom",16);
        var p2 = new Person("bob",19);
        console.log(p1,p2);
    </script>

2,继承模式

2.1, 组合继承call( )

(1)含义:

ES6之前并没有给我们提供 extends 继承。我们可以通过构造函数+原型对象模拟实现继承,被称为组合继承

(2)语法:

// 调用这个函数,并且修改函数运行时 this 指向
fun.call(thisArg, arg1, arg2, …)
thisArg:当前调用函数 this 的指向对象。
arg1,arg2:传递的其他参数。

(3)例如:

  <script>
      function fn(x, y) {
          console.log(this);
          console.log(x + y);
      }
      var o = {
          name: 'adele'
      }
      fn(1, 2);    //分别为window对象和3     this指向window
      //1,fn.call( )可以调用函数
      // 2,fn.call( ) 可以改变这个函数的this 指向,此时这个函数的this就指向了o这个对象。
      fn.call(o, 1, 3);     //输出:{name: "adele"} 和 4
    </script>

(4)作用:

1,可以调用普通函数
2,可以改变this的指向。

2.2,借用父构造函数 + call来继承属性

(1)含义:

通过 call( ) 把父类型的 this 指向子类型的 this,这样就可以实现子类型继承父类型的属性。

(2)例如:

 <script>
      // 父构造函数
      function Father(name, age) {
          this.name = name;
          this.age = age;
      }
      // 子构造函数
      function Son(name, age, sex) {
          // 通过call函数改变Father函数内部this的指向,使其指向Son构造函数创建的实例对象ldh
         // 从而借用父构造函数的代码为子构造函数创建的实例对象赋值,实现了逻辑上的继承属性的
          关系。
          Father.call(this, name, age)
          this.sex = sex;
      }
      var ldh = new Son('刘德华', 18, '男')
      console.log(ldh);
      console.log(ldh.sex);  // 男
  </script>

2.3,借用原型对象来继承方法

(1)例如:

 <script>
      function Father( ) {
          this.run = function( ) {
              console.log('父亲喜欢跑步');
          }
      }
      Father.prototype.work = function( ) {
          console.log('父亲要工作');
      }
      function Son( ) {
      }
      // 错误方式,该代码会导致子原型对象指向父原型对象的地址,如果更改子原型对象,则实际更改的是父原型对象。
      // Son.prototype = Father.prototype
      // 使子原型对象指向一个父实例对象,便拥有了父构造函数的方法以及父构造函数的原型对象上的方法。
      Son.prototype = new Father();
      // 小问题:上述直接通过对象覆盖了子原型对象,导致了constructor指向的构造函数的改变,需要单独为其赋值。
      // console.log(Son.prototype.constructor); 指向father ,需要指向son
      Son.prototype.constructor = Son;

      Son.prototype.exam = function( ) {
          console.log('孩子要考试');
      }
      var ldh = new Son();
      console.log(ldh);
      // 打印父原型对象,正确的结果不应该包含子原型对象的方法
      console.log(Father.prototype);
      // 验证是否实现了继承
      ldh.work( );  // 父亲要工作
      ldh.run( );    // 父亲喜欢跑步
      // 整个过程的描述:
      // 1.先在ldh对象上找work( )和run( )
      // 2.若找不到则在ldh的__proto__,即父实例对象身上找work( )和run( )
      // 3.若还是找不到则在父构造函数的原型对象上找,最终找到了work( )和run( )
      // 通过构造函数和原型对象实现继承的原理:(充分利用了原型链)
      // 1.子对象在找不到方法时会去原型对象上找方法,
      // 2.让子构造函数的原型对象指向父实例对象,则相当于子对象会去父实例对象上找方法。
      // 3.若在父实例对象上找不到方法,则会去父构造函数的原型对象上找这个方法,只要找到了方
     法则说明继承了父方法。
  </script>

2.4,类的本质

(1)含义:

// ES6之前通过构造函数+原型实现面向对象编程

1,构造函数有原型对象 prototype
2,构造函数原型对象 prototype 里面有 constructor 指向构造函数本身
3,构造函数可以通过原型对象添加方法
4,构造函数创建的映例对象有__proto__原型指向构造函数的原型对象

// ES6通过类实现面向对象编程

1,类 class 本质还是函数 function,我们也可以简单的认为类就是构造函数的另外一种写法。
2,类的所有方法都定义在类的 prototype 属性上。
3,类创建的实例里面也有 __proto__ 指向类的 prototype原型对象
4,所以ES6的类它的绝大部分功能,ES5都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。
5,所以ES6的类其实就是语法糖。
6,语法糖:语法糖就是一种便捷写法、简单理解,有两种方法可以实现同样的功能,但是一种写法更加清晰、方便那么这个方法就是语法糖。

(2)例如:

  <script>
      class Star {
      }
      Star.prototype.sing = function() {
          console.log('sing a song');
      };
      // 1.类的实质是函数,相当于构造函数
      console.log(typeof Star); //function
      // 2.类同构造函数一样,拥有原型对象
      console.log(Star.prototype);
      //3.类同构造函数一样,其原型对象上拥有constructor属性,该属性指向了类(构造函数)
      console.log(Star.prototype.constructor);
      var adele = new Star();
      //4.类的实例对象同构造函数的实例对象一样,拥有__proto__属性,该属性指向了类的原型对象
   (构造函数的原型对象)
      console.log(adele.__proto__);
      // 综上,ES6中的类其实是构造函数的语法糖,ES6中类的绝大多数功能都能通过ES5中的构造函
    数和原型对象实现
  </script>

三,ES5 中新增的方法

1,ES5 新增方法概述

(1)含义:

ES5中给我们新增了一些方法,可以很方便的操作数组或者字符串,这些方法主要包括:数组方法、字符串方法和对象方法。

(2)方法:

ES5数组新增的方法:forEach( ),filter( ),map( ),some( ),every( )
find()方法主要用来返回数组中符合条件的第一个元素(没有的话,返回undefined)(ES6)
map()方法主要用来对数组中的元素调用函数进行处理,并且把处理结果放在一个新数组中返回(如
果没有返回值,新数组中的每一个元素都为undefined)(用法跟filter类似)
every()方法用于检测数组中所有元素是否都符合指定条件,若符合返回true,否则返回 false; (跟 some用法相似)

2,数组方法

2.1,forEach 迭代(遍历)数组

(1)含义:

forEach( )方法遍历数组。无返回值,用于内部操作,比如作加法器,动态添加表格元素,循环绑定事件。

(2)语法:

array.forEach(function(currentValue, index, arr) { } )
currentValue:数组当前项的值(元素)。
index:数组当前项的索引。
arr:数组对象本身。

(3)例如:

  <script>
      var arr = [1, 2, 3, 4, 5]
        var sum = 0
         arr.forEach(function(value, index, array) {
             console.log('每个数组元素:' + value);
             console.log('每个数组元素的索引号:' + index);
             console.log('数组本身' + array);
             sum += value
           })
         console.log(sum);  // 15
  <script>

2.2,filter 筛选数组

(1)含义:

filter( ) 方法创建一个新数组,新数组中的元素是通过检查指定数组中符合条件的所有元素,主要用于筛选数组。

(2)语法:

array.filter(function(currentValue, index, arr) { } )
currentValue:数组当前项的值(元素)。
index:数组当前项的索引。
arr:数组对象本身。

(3)注意:

1,它直接返回一个新数组。
2,把所有满足条件的数组元素返回回来。

(4)例如:

 <script>
      var arr = new Array(2, 3, 4, 6, 9);
      var newArr = arr.filter(function (value, index, array) {
        // 满足条件的每一个元素会被添加到一个新数组中,最后会返回新生成的数组
        return value % 2 === 0;
      });
      console.log(newArr);   // [2, 4, 6]
  </script>

2.3,some 查找数组中是否有满足条件的元素

(1)含义:

some( )方法用于检测数组中的元素是否满足指定条件,通俗点就是查找数组中是否有满足条件的元素。

(2)语法:

array.some(function(currentValue, index, arr) { } )
currentValue:数组当前项的值(元素)。
index:数组当前项的索引。
arr:数组对象本身。

(3)注意:

1,它返回值是布尔值,如果查找到这个元素,就返回 true,如果查找不到就返回 false
2,如果找到第一个满足条件的元素,则终止循环,不在继续查找。

(4)例如:

  <script>
      var arr = ["apple", "banana", "orange"];
      var boolean = arr.some(function (value, index, array) {   // 后面两个参数可以省略
    // 当查找到该元素时,直接return一个true值,后面的元素不会再查找比较。可以通过浏览器
    调试工具debug验证
        return value === "banana";  
      });
      console.log(boolean);     // true
    </script>

3,字符串方法

3.1,trim 方法去除字符串两侧空格

(1)含义:

trim( ) 方法会从一个字符串的两端删除空格字符。

(2)语法:

str.trim( )

(3)注意:

trim( ) 方法并不影响原字符串本身,它返回的是一个新的字符串。

(4)例如:

   var str = ‘     ldh    ’
   var newstr = str.trim( );
   console.log(newstr);     // ldh

4,对象方法

4.1,Object.keys 方法遍历对象属性

(1)含义:

Object.keys( ) 用于获取对象自身所有的属性。

(2)语法:

Object.keys(obj)

(3)注意:

1,返回一个由属性名组成的数组。
2,效果类似 for…in

(4)例如:

  <script>
      var obj = {
          name: 'ldh',
          age: 12,
          sex: '男'
      }
      console.log(Object.keys(obj));      //['name','age','sex']
      // 通过Object.keys可以获取一个对象含有的属性的个数
      console.log(Object.keys(obj).length);
  </script>

4.2,Object.defineProperty( )

(1)含义:

Object.defineProperty( ) 定义新属性或修改原有的属性。

(2)语法:

Object.defineProperty( obj, prop, descriptor )
descriptor说明:以对象形式{ } 书写 value:设置属性的值,默认为underfined
writable:值是否可以重写。truefalse,默认为false
enumerable:目标属性是否可以被枚举。true或者false
configurable:目标属性是否可以被删除或是否可以再次修改特性true或者false,默认为false

(3)例如:

 <script>
      var obj = {
          id: 1,
          name: 'xiaoming',
      };
      // 1,原始的添加属性的方式
       obj.age = 19

      // 2,通过Object.defineProperty添加属性
      //注意sex需要加引号,若sex存在,则覆盖;不存在,则添加。
      Object.defineProperty(obj, 'sex', {
          value: '男',
          writable: true, 
          //是否可以重写,默认值为false
          enumerable: true, 
          //是否可以通过Object.keys()遍历,默认为false
          configurable: false
          //是否可以删除该属性以及修改enumerable和configurable,默认为 false
      });
      obj.sex = '女';

      delete obj.sex; //若configurable为false,则删除是无效的
      //若configurable为false,则下面的代码会报错
      // Object.defineProperty(obj, 'sex', {
      //     enumerable: false,
      //     configurable: true
      // });
      console.log(obj);
      console.log(Object.keys(obj));
      Object.defineProperty(obj, 'age', {
          value: 22,
          // writable: false
      });
      console.log(obj);
      obj.age = 23;
      console.log(obj);
      console.log(Object.keys(obj));
      delete obj.age;
      console.log(obj);
  </script>

(4)注意:

1,直接通过对象.属性 方式创建的属性,其值可以被重写,可以被遍历出,可以被删除。
2,通过Object.defineProperty( )创建的属性默认是重写无效,无法被遍历,无法被删除的。

以上是关于JavaScript系列之高级篇的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript系列之高级篇

[JavaScript从入门到放弃系列] 高级篇11.Vue.js之路由part2

JavaScript系列:高级函数篇

JavaScript系列之Web API篇

JavaScript系列之基础篇

软考高级系统架构设计师系列论文之:软考高级架构设计师百篇范文