JavaScript系列-(原型-原型连-call-apply-继承)

162 阅读4分钟

求一个字符串的字节长度

注意点:汉字的字节长度是两个

// 求一个字符串字节长度(汉字是两个字节)
function bytesLength(str) {
    var count = 0;
    for(var i=0; i<str.length;i++) {
        if(str.charCodeAt(i) > 255) {
            count += 2;
        }else {
            count ++;
        }
    }
    return count;
}

原型

定义: 原型是function对象的一个属性,它定义了构造函数制造出的对象的公共祖先。通过该构造函数产生的对象可以继承该原型的属性和方法 (原型也是对象)

可以利用原型的特点和概念,可以提取共有属性

proto&构造函数&new(object)

//Person的对象-原型对象
Person.prototype.name = "abc";
//构造函数 Person
function Person() {
    // var this = {
    //      _proto_: Person.prototype  
    //}
}
var person = new Person();

在new person时 发生:

this -> _proto:Person.prototype()-原型对象 可以person可以调用原型中的属性和方法 person.proto = obj(可以被修改-之后就会继承obj的内容)

插入一个小问题:代码

Person.prototype.name = "sunny";
function Person() {
    // var this = {
    //      _proto_: Person.prototype  
    //}
}
var person = new Person();

Person.prototype = {
    name:"cherry"
}
console.log(person.name) // sunny

原因(对象空间的引用或者指向)

  1. person.name -> this.proto(Person.prototype原型)上的name属性
  2. Person.prototype -> 一个对象空间 -> {name:"sunny"}
  3. person.name -> this.proto -> 一个对象空间 -> {name:"sunny"}
  4. 修改指向引用 Person.prototype -> 一个对象空间 -> {name:"cherry"} 5.由于person.name 先new -> this.proto -> 的指向空间没有变 -> {name:'sunny'} 所以:person.name = 'sunny'

如果纯粹修改Person.prototype.name = "cherry"; 则person.name = 'cherry'

原型连

概念:new对象沿着作用域链去查找属性和方法的 链 引出:

1

// 如果要求打印出son.lastName
son this._porto_: Son.prototype

father this._porto_: Father.prototype

grand this._porto_: Object

由于 son this._porto_ -> father this._porto_ -> father grand._porto_ -> {lastName,Object.prototype} 所以son.lastName 顺这链找到了grand.lastName = "Deng"

console.log(son.lastName) // "Deng"

Object.create()

创建对象 Object.create(原型) 创建对象、指定原型

var obj = { name:'sunny'}
var obj1 = Object.create(obj)

// obj1 = {} 并且obj1的原型是obj obj1.name= "sunny"

不是所有对象都是继承自Object Object.create(null)是特例

判断类型

Object.prototype.toString.call(123)
"[object Number]"

call/apply

作用: 改变this指向 区别: 后面传的参数形式不同

  1. 改变this指向1
function Person(name, age) {
    // Person.call() 会将this -> obj
    this.name = name;
    this.age = age;
}

var person = new Person("deng", 100)
var obj = {};
Person.call(obj, "cheng",300) //改变this指向 借用方法

  1. 改变this指向2
function Person(name, age, sex) {
    this.name = name;
    this.age = age;
    this.sex = sex;
}

function Student(name, age, sex, tel, grade) {
    // var this = Student
    Person.call(this, name, age, sex); //Student则有了Person的属性
    this.tel = tel;
    this.grade = grade;
}

var student = new Student('sunny',123,'male',139,2017)

student -> {name: "sunny", age: 123, sex: "male", tel: 139, grade: 2017}
  1. call/apply传参形式不同 Person.apply(obj, ["cheng",300]) Person.call(this, [name, age, sex]);

继承那点方式

  1. 传统的形式-> 原型链

    过多的继承了没有用的属性

    // Animal构造函数
    function Animal(){ }
    // Animal原型山添加一个属性species
    Animal.prototype.species = "动物";
    
    // 将Cat的prototype对象指向Animal的prototype对象
    Cat.prototype = Animal.prototype;
    
    // 原型的修改会导致Cat的构造器的修改,所以需要将cat的构造器重新指向Cat它自己
    function Cat(){ }
    Cat.prototype.constructor = Cat;
    var cat1 = new Cat("大毛","黄色");
    // cat的对象中就有了species这个属性
    alert(cat1.species); // 动物
    
  2. 借用构造函数(call/apply)

    不能继承借用构造函数的原型 每次构造函数都要多走apply/call函数

    function Person(name, age, sex) {
        this.name = name;
        this.age = age;
        this.sex = sex;
    }
    
    function Student(name, age, sex, tel, grade) {
        // var this = Student
        Person.call(this, name, age, sex); //Student则有了Person的属性 用apply也行
        this.tel = tel;
        this.grade = grade;
    }
    var student = new Student();
    
  3. 共享原型(多个构造函数共用一个原型)

    一个原型修改另一个也在修改

    // 标准方法
    function inherit(Target, Origin) {
        Target.prototype = Origin.prototype;
    }
    
  4. 圣杯模式(完善)

    function Father() {}
    function Son() {}
    
    function F() {} // 中间层
    F.prototype = Father.prototype;
    
    // son就形成了继承
    Son.prototype = new F();
    
    //如果一个修改这样不会影响另一个(因为new F是独立存在的一个空间)
    

    标准方法:

    function inherit(Target, Origin) {
        function F() {};
        F.prototype = Origin.prototype;
        
        Target.prototype = new F();
        // 构造器重新修改过来
        Target.prototype.constructor = Target;
        // 继承自谁(保存值)
        Target.uber = Origin.prototype;
    }
    

    高级写法(一般是库写法)

    私有化属性(形成了闭包,变量引用与保留了inherit() 上下文)

    var inherit = (function () {
        var F = function() {}
        return function (Target, Origin) {
           F.prototype = Origin.prototype;
        
           Target.prototype = new F();
           // 构造器重新修改过来
           Target.prototype.constructor = Target;
           // 继承自谁(保存值)
           Target.uber = Origin.prototype;
        }
    }())
    
  5. 深拷贝继续(数组和对象)

    function deepCopy(p, c) {
    &emsp;&emsp;&emsp;var c = c || {};
    &emsp;&emsp;&emsp;for (var i in p) {
    &emsp;&emsp;&emsp;&emsp;&emsp;if (typeof p[i] === 'object') {
    &emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;c[i] = (p[i].constructor === Array) ? [] : {};
    &emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;deepCopy(p[i], c[i]);
    &emsp;&emsp;&emsp;&emsp;&emsp;} else {
    &emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;c[i] = p[i];
    &emsp;&emsp;&emsp;&emsp;&emsp;}
    &emsp;&emsp;&emsp;}
    &emsp;&emsp;&emsp;return c;
    }