• 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("Tom");

        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 = [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 = [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 = [];

            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 = [], 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")

     

  • 相关阅读:
    s2sh的MVC执行流程和执行原理
    码支付-个人支付接口-个人收款
    notepad++ FTP同步插件【FanFtpTools】2.0
    自己做的notepad++ FTP同步插件【FanFtpTools】
    thinkphp高并发抢购代码测试-解决高并发下的超卖问题!
    thinkphp5.0 与thinkphp5.1 db()函数的区别
    thinkphp5.1 长连接-单例模式测试之二
    thinkphp5.1长连接-单例模式测试!
    php 全文搜索引擎-讯搜使用
    在线WEB开发编辑器,edt.df5d.com
  • 原文地址:https://www.cnblogs.com/Tabb/p/6439274.html
Copyright © 2020-2023  润新知