首页 > 代码库 > JavaScript (JS) 面向对象编程 浅析 (含对象、函数原型链解析)

JavaScript (JS) 面向对象编程 浅析 (含对象、函数原型链解析)

1. 构造函数原型对象:prototype

构造函数独立创建对象,消耗性能

    function Person(name) {

        this.name = name;

        this.sayHello = function () {

            console.log("Hello,my name is " + this.name)

        }

    }

    var P1 = new Person("Tom");

    var P2 = new Person("Jim");

    P1.sayHello();

    P2.sayHello();

    console.log(P1.sayHello == P2.sayHello)   返回值为false

返回值为false说明通过同一个构造函数独立创建的两个构造函数对象P1和P2的sayHello方法不相等,说明他们分别在内存中开辟了存储空间,消耗性能

prototype:每一个函数都会有一个prototype属性

将同一个构造函数创建出的不同对象的不同函数方法,创建在该构造函数的prototype属性中,可实现只创建一次,所有该构造函数对象都可以调用这一方法

技术分享

prototype是构造函数的原型属性,也是构造函数对象例如图中P1、P2的原型对象

prototype代码演示

1)动态添加方法属性,方法是独立分布添加上的,保留prototype指向构造函数的constructor属性

技术分享

function Person(name) {

        this.name = name;

    }

    Person.prototype.sayHello = function () {

        console.log("sayHello");

    }

    Person.prototype.study = function () {

        console.log("study");

    }

    Person.prototype.goHome = function () {

        console.log("goHome");

    }

    var P1 = new Person("Tom");

    var P2 = new Person("Jim");

    var P3 = new Person("Jack");

    P1.sayHello();

    P2.study();

    P3.goHome();

2)直接替换,创建一个对象添加方法属性,原本指向构造函数的constructor属性消失,        该替换添加在构造函数实例对象声明之前

技术分享

 function Person(name) {

        this.name = name;

    }

Person.prototype = {

    constructor:Person,      手动添加constructor属性

        sayHello: function () {

            console.log("sayHello");

        },

        study: function () {

            console.log("study");

        },

        goHome: function () {

            console.log("goHome");

        }

    }

    var P1 = new Person("Tom");    声明构造函数对象

    var P2 = new Person("Jim");

    var P3 = new Person("Jack");

    P1.sayHello();

    P2.study();

    P3.goHome();

__proto__:实例对象访问原型属性(双下划线)  IE8以下不支持

构造函数通过prototype访问原型

实例对象通过__proto__访问原型

(1)示例代码如下:

        var object = {};

        object.name = "Tom";

        object.age = 18;

        object.__proto__ = {

            hello: "hello",

            color: "red"

        }

        console.log(object.hello);

通过这个方法,可以给实例对象统一添加方法属性

(2)兼容性处理   IE8兼容处理

利用构造函数,访问constructorprototype,这样就可以访问原型

    function Person(name) {

        this.name = name;

    }

    function __getProto__(object) {

        return object.constructor.prototype;

    }

    var ob = new Person("Jack");

console.log(__getProto__(ob) === ob.__proto__);    返回值为true

2. 继承

HTML对象原型继承关系,示例div:

技术分享

② 混入

通过给对象添加一个extend方法,动态给对象添加属性

  arguments: 函数中默认的类似数组类型对象,里面存储了所有传入的参数

相关代码示例如下:

    var object = {

        extend: function (obj) {

            for (var i = 0; i < arguments.length; i++) {

                for (var k in arguments[i]) {

                    this[k] = arguments[i][k];

                }

            }

        }

    }

    object.extend({name: "Tom"}, {age: 20, gender: "male"}, {

        sayHello: function () {

            console.log("Hello");

        }

    });

console.log(object)

混合式继承   通过给构造函数的原型属性添加方法实现

示例代码如下:

    function Person() {

        Person.prototype.extend = function (obj) {

            for (var i = 0; i < arguments.length; i++) {

                for (var k in arguments[i]) {

                    this[k] = arguments[i][k];

                }

            }

        }

    }

    var p = new Person();

    p.extend({name: "Tom"}, {age: 20, gender: "male"}, {

        sayHello: function () {

            console.log("Hello");

        }

    });

console.log(p);

Object.create( )  创建对象    IE8以下不支持该方法

示例代码如下:

    var o = {name: "Tom", age: 20, gender: "male"};

    var obj = Object.create(o);

    console.log(obj);

技术分享

 

 

 

 

 

创建的对象obj的属性和方法均继承自传入的对象o

Object.create( ) 方法的实现原理如下:

    Object.create = function (obj) {

        function F() { };

        F.prototype = obj;

        return new F();

}

IE8兼容性封装:

    function createWithObject(obj) {

        if (Object.create) {

            return Object.create(obj);

        } else {

            function F() { };

            F.prototype = obj;

            return new F();

        }

}

3. 对象的原型链  (构造函数)

示例代码如下:

    function Person(name, age, gender) {

        this.name = name;

        this.age = age;

        this.gender = gender;

    };

    function Student() { };

    Student.prototype = new Person("Tom", 18, "male");

var student = new Student();

function Instance (){ };     所有构造函数都指向相同的原型

以上代码所对应的原型链结构示意图如下:

技术分享

 

 

 

 

 

 

 

 

 

 

4. Function创建函数

Function是一个构造函数  new Function得到一个函数

new Function(arg0,arg1,…,argN,body );最后一个参数body是函数执行的代码块(函数体),其他参数都是传入函数代码块的参数

代码示例如下:

var getSum = new Function(num1” , ”num2 , ”return num1+num2”);

函数代码块、函数体中内容太多时书写方法:

1.直接书写:  高斯求和函数

 var getSum = new Function(min” , ”max , ” var sum = 0;for (var i = min; i <= max; i++) { sum = sum + i;}return sum;”);

2.字符串拼接书写:

    var getSum = new Function("min", "max",

            "var sum = 0;" +

            "for (var i = min; i <= max; i++) {" +

            "sum = sum + i;" +

            "}" +

            "return sum;");

3.获取HTML中的value书写:

封装一个tool函数:

    function tool(idName) {

        var ele = document.getElementById(idName);

        var code = ele.innerText;

        ele.parentNode.removeChild(ele);

        ele = null;

        return code;

}

将需要传入的参数内容写入HTML中:

   <div id="div">

    var sum = 0;

        for (var i = min; i <= max; i++) {

          sum = sum + i;

    }

    return sum;

</div>

再创建函数:

var getSum = new Function("min", "max",tool("div"));

5.函数的原型链

js中任何函数都是Function的实例

console.log(Function)   结果:function Function() { [native code] }

函数的基本原型链结构如下图所示:

技术分享

 

 

 

 

 

 

 

 

 

6.完整的原型链结构图(对象与函数)

技术分享

 

      

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

instanceof:判断构造函数的原型属性是否在该对象的原型链上

    function Person() {};

    var p = new Person();

    console.log(p instanceof Object)             返回ture

    console.log(p instanceof Person)            返回ture

    console.log(Person instanceof Function)       返回ture

console.log(Person instanceof Object)        返回ture

不应该过多依赖原型链继承,非常消耗性能,解决办法如下:

    var o = {

        method: function () {

            console.log("这是一个方法");

        }

    }

    function Person() { };

    Person.prototype = o;

    function Student() {

        this.method = Person.prototype.method;   通过这种方式快速访问method

    }

    Student.prototype = new Person();      这样访问消耗性能,可以省略

    var s = new Student();

    s.method();

7.闭包   js中函数是具有作用域隔离特性的内存结果,可以作为一个闭包

返回函数

    function func() {

        var num = Math.random();

        function fn() {

            console.log(num);

            return num;

        }

        return fn;

    }

    var foo = func();

    var f1 = foo();

var f2 = foo();

因为在func中返回了fn,所以func只会被调用一次,fn被调用两次,打印结果相同

沙箱模式    一个隔离的执行环境

1)递归函数性能优化,以斐波拉契数列为例:

    var count1 = 0;

    function fib1(n) {

        count1++;

        if (n == 1 || n == 2) {

            return 1;

        } else {

            return fib1(n - 1) + fib1(n - 2);

        }

    }

    console.log(fib1(30));

    console.log(count1);

    var count2 = 0;

    var data = http://www.mamicode.com/[1, 1];   声明一个数组接收创建的fib(n)

    function fib2(n) {

        count2++;

        if (data[n-1]) {       如果数组中有值,直接返回该值

            return data[n-1];

        } else {

            return data[n-1] = arguments.callee(n - 1) + arguments.callee(n - 2);

        }

    }

    console.log(fib2(30));

    console.log(count2);

 技术分享

通过比较count1与count2的值可以发现,使用数组接收存储fib2(n)值的函数递归次数明显小于直接递归的fib1函数,性能得到极大优化

对斐波拉契数列函数进行闭包封装:封装一个自执行函数fib

    var fib = (function () {

        var data = http://www.mamicode.com/[1, 1];

        return function(n) {

            if (data[n - 1]) {

             return data[n - 1];

            } else {

             return data[n - 1] = arguments.callee(n - 1) + arguments.callee(n - 2);

            }

        }

})();

 2)闭包应用:封装一个带有事件添加和移除的函数

    var tabbLoad = (function () {

        var data = http://www.mamicode.com/[];

        window.onload = function () {

            for (var i = 0; i < data.length; i++) {

                data[i]();

            }

        }

        return {

            addEvent: function (fn) {

                data.push(fn);

            }, removeEvent: function (fn) {

                for (var i = data.length - 1; i >= 0; i--) {

                    if (fn.toString() === data[i].toString()) {

                        data.splice(i, 1);

                    }

                }

            }

        }

})();

    tabbLoad.addEvent(function () {

        console.log("执行函数1")

    })

    tabbLoad.addEvent(function () {

        console.log("执行函数2")

    })

    tabbLoad.removeEvent(function () {

        console.log("执行函数2")

    })

3)闭包应用:创建一个可以缓存的函数cache

    var createCache = (function () {

        var data = http://www.mamicode.com/[], max = 3; max为设置的缓存最大值,在查看data数据时可以将var去掉,将data变为全局变量,但在实际应用时一定不能去掉

        function cache(key, value) {

            data.push(key);

            cache[key] = value;

            if (data.length > max) {

                var temp = data.shift();

                delete cache[temp];

            }

        }

        return cache;

    })();

    cache("name1","Tom")

    cache("name2","Jim")

    cache("name3","Jack")

    cache("name4","Lily")

技术分享

 

JavaScript (JS) 面向对象编程 浅析 (含对象、函数原型链解析)