JavaScript高级

面向对象

  好处:易于分工与合作,适合大型项目开发;健壮性强,易于维护

  特征:封装、继承、多态      

1. 原型

  1. 学习原型理由

    问题:每创建一个对象,都会在对象中添加一个方法,方法是不变的,若创建每个对象会造成内存浪费。

    解决:把方法抽取出来,让所有 实例对象共享。

    实现共享:使用原型(把方法封装到原型中)

  2. 什么是原型

    原型也是一个对象

    1. 获取原型的方式:构造函数名.prototype

    2. 构造函数与原型的关系

      * 构造函数可以通过prtotype找到原型

      * 原型可以通过construcror找到构造函数

  3. 原型链

    实现对象在调用一个属性和方法时过程如下:

      ①会先从实例对象中找

      ②若自身没有找到则通过__prot__找到原型,去原型中找

      ③若原型中没有,则通过原型的__proto__找到原型的原型去找

      之所以会找到因为原型链的存在

2. 继承

  1. 利用原型实现继承

    更改子类的原型指向(或给子类的原型重新赋值)

//定义person构造函数
function Person(){
  this.name = '名字';
  this.age = 10;
  this.gender = '男';      
}
//给构造函数原型添加方法
Person.prototype.sayhi = function(){
     console.log('hi')
}
//实例化对象p1
var p1 = new Person()

//定义student构造函数
function Student(){
    this.stuId = '10100';
}

Student.prorotype = p1;

//实例化学生对象
var stu1 = new Student();
console.log(s1.name);     //输出name值是p1.name的值

    原型继承的问题和优点:

      缺点:继承的属性是没有意义的;

      优点:继承的方法是有意义的;

  2. 借用继承

    call方法改变this指向

    函数体内的this指向是在函数调用时决定的(指向调用者)

    语法:函数名.call( 调用者,参数 )

    作用:该函数会立即执行,但函数体内的this会指向借用者

function fn(n, a){
    this.name = n;
    this.age = a;
}
//定义对象
var obj1 = {type: 'obj'};
//fn借用给obj1
fn.call( obj1, '展示', 11)

    call方法实现借用

      在子类构造函数中借用父类构造函数,并且在 call 中传入子类对象

//per构造函数
function Per(n,a){
    this.name = n;
    this.age = a;
}

Per.prototype.sayhi = function(){
  console.log(this.name + 'hi')      
}
//stu构造函数
function Stu(n,a){
    this.sid = '10001'
    Per.call(this, n, a)    //Per谁调用借给谁
}
var s1 = new Stu('栈', 23)
console.log(s1.name);     //输出栈

    借用继承的问题和优点

      缺点:仅仅借用了属性(原因:call方法在被per类调用执行时仅仅让调用者执行函数内部的程序,函数外的没有借用执行)

      优点:很好的继承属性

      解决:原型+借用

  3. 组合继承

    原型:借用方法    +   借用:继承属性

//创建per构造函数
function per(n, a){
  this.name = n;
  this.age = a;
}
//为per添加原型
per.prototype.sayhi = function(){
    console.log(this.name + 'hi')
}

//创建stu构造函数
function stu(n,a){
    this.sid = '1001011'
    //借用属性
    per.call(this, n, a)
}
//借用方法
stu.prototype = per.prototype
var s1 = new stu('组件', 22)
console.log(s1.name)     //输出组件
s1.sayhi()    //输出组件hi
    

    组合继承问题:

      缺点:stu类和per指向同一个原型,stu的原型改变时人类的原型也会被改变

      优点:让他们不要指向同一个原型,使用各自的原型(对象之间的拷贝)

  4. 对象之家的拷贝

    对象B拷贝对象A的属性和方法

//一个对象拷贝另一个对象中的属性和方法,parentObj父对象  childObj子对象
function copy(parentObj, childObj){
    for(var key in parentObj){
        if( childObj[kye] == undefined ){
            childObj[key] = parentObj[key];
        }
    }
}
//对象A
var wjl = {
    name: '大熊',
    money: 100,
    house: '大屋子',
    cars: ['qq', '11'],
    sayhi: function(){
        console.log('大家好,我叫'+this.name);
    }
}
//对象B
var wsc = {
    name: '小熊'
}
copy(wjl,wsc)

  5. 拷贝+ 借用组合继承

    拷贝方法 + 借用属性

    // 【人类】
    function Person(name, age, gender) {
      this.name = name;
      this.age = age;
      this.gender = gender;
    }
    Person.prototype.sayHi = function () {
      console.log('大家好....O(∩_∩)O哈哈~,我叫' + this.name);
    }
    Person.prototype.eat = function () {
      console.log('我会吃饭.....');
    };

    // 【学生类】
    function Student(name, age, gender) {
      // this代表谁?代表一个学生的实例对象 s1、s2
      this.stuId = '10010';
      // --借用函数--Person[仅仅借用函数体中内容,并没有借用函数体之外其他程序]
      Person.call(this, name, age, gender)
    };

    // [拷贝人类原型(wjl)中的方法给学生原型(wsc)]
    copy(Person.prototype, Student.prototype);
    Student.prototype.test = function() {
      console.log('我超爱考试');
    }
    // 【老师类】
    function Teacher(name, age, gender) {
      // 借用继承-借用人类的属性
      Person.call(this, name, age, gender);
    }
    // [拷贝人类原型(wjl)中的方法给老师原型(wsc)]
    copy(Person.prototype, Teacher.prototype);

    // 创建学生对象
    var s1 = new Student('张三',19,'男');
    // 创建老师对象
    var t1 = new Teacher('李四',39,'男');

3. this指向

  普通函数中this指向window

  构造函数中this指向当前所创建的对象 

  定时器中this指向window

  对象方法中的this指向调用者

  事件处理程序中this指向事件源

4. 改变函数内部this指向

  1. apply

    语法:函数名.apply( 调用者,[参数......] )

    作用:函数被借用时,会立即执行,并且函数体内的this会指向借用者

  2. bind方法

    语法:函数名.apply(调用者, 参数.......)

    作用:函数被借用时,不会立即执行,返回新的函数。函数体内this指向调用者

5. 闭包

  1. 什么是闭包

    闭包就是能够读取/设置  他函数 内部变量的函数;闭包就是将函数内部和函数外部链接起来的桥梁。

  2. 闭包的用途

    可以在函数外部读取函数函数内部成员;让函数内成员始终活在内存中(延长变量生命周期)

  3. 如何实现闭包

    ①函数嵌套

    ②外层函数,必须要有局部变量

    ③内部函数必须要操作外层函数的局部变量,并且在外层中返回内层函数或内层函数在外部被间接的调用。

    调试:在内层函数中设置断点,若进入来在右侧中的scope下可以看到closure,就表示产生了闭包。  在实际开发中,不要可以使用闭包,因为延长生命周期的变量可能会一直存活在内存中。  解决方案:把外层变量设置为null

6. 递归

  作用:减少代码量

  缺点:若递归的层数比较多时,会消耗CPU。浪费内存,影响性能。

若将来递归层较少时,可以选择用递归方式。

    

  

    

原文地址:https://www.cnblogs.com/xhrr/p/11172927.html