• 高程三中的原型部分以及其中提到的构建模式


    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <title>Page Title</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
    </head>
    <body>
        <script>
            //写在前面:创建的每个函数都有一个属性:prototype,这个属性是一个指针,指向原型对象
            //原型对象的用途:包含可以由特定类型(Object,Array,Function以及各种自定义)的所有实例共享的属性与方法
            //或者说:prototype就是通过 调用构造函数构建的 那个对象实例 的原型对象 
            //这样就不必在构造函数中定义对象实例的信息,直接将这些信息添加到原型对象中即可    
            function Create(){};
            Create.prototype.name = "Harold";//通过Create.prototype来为构造函数的原型对象添加方法
            Create.prototype.method = function(){
                // alert("upup就完事了!");
            };
            var obj1 = new Create();
            var obj2 = new Create();
            obj1.method();
            console.log(obj1.method() == obj2.method() );//true
            //两个对象访问的是同一组属性与方法
            //如何理解原型对象?
            //无论何时,只要创建了一个函数,就会根据一组特定的规则,为该函数创建一个prototype属性(指向函数的原型对象)
            //默认情况下,所有原型对象都会获得一个constructor属性,这个属性是一个指向prototype属性所在函数的指针 
            // Person.prototype.construtor指向Person 而Person的prototype指向PersonPrototype 高程三P148
            //在创建了自定义的构造函数Func后,其原型对象默认只会取得constructor属性,其他均有Object继承而来
            //调用构造函数创建一个实例后,该实例的内部会包含一个指针,[[prototype]](属性_proto_)
            //这个连接/关系存在于实例与构造函数的原型对象间,而非实例与构造函数间
            //新实例的[[prototype]]/_proto_、构造函数的prototype属性均指向 Person.prototype
            //注意在上面的实例中我们可以直接调用person.method() 这就是通过查找对象属性的过程来实现的
            console.log(Create.prototype.isPrototypeOf(obj1));//true 用isPrototypeOf()方法判断 (这是原型对象的方法)
            console.log(Object.getPrototypeOf(obj1));//Object method: ƒ ()name: "Harold"constructor: ƒ Create()__proto__: Object
            ///这个方法返回[[prototype]] / _proto_的值(即obj1的原型对象),== Create.prototype 
            //注意:每当代码读取对象的某个属性,都会执行一次搜索,先在实例中搜索,如果没有找到,则继续前往[[prototype]]指针指向的
            //原型对象中搜索  这是多个对象实例共享原型所保存的属性和方法的基本原理
    
            //可以通过对象实例访问保存在原型中的值 但不能这样重写原型中的值  只会屏蔽 而不会覆盖 联系行37
            obj1.name = "budu";
            console.log(obj1.name);
            //使用delete操作符可以完全删除实例属性
            delete obj1.name;
            console.log(obj1.name);
            //hasOwnProperty()方法可以检验一个实例是否自己拥有给定的属性/方法
            console.log(obj1.hasOwnProperty("name"));//false
            //原型与in操作符:in操作符的两种使用方式,单独使用与在for-in循环中使用
            //单独使用时,in操作符会在能够通过对象访问给定的属性时返回true
            console.log("name" in obj1);//true 不论通过原型还是实例,只要能访问到就true
            //配合hasOwnProperty()方法 可以直接确定这个属性在实例上还是原型对象上
            function hasPrototypeProperty(object,attrname){
                return !object.hasOwnProperty(attrname) && (attrname in object);
                //T && T = T 不在实例上且能访问到-在对象上 真·hasPrototypeProperty  F && T = F
            }
            console.log(hasPrototypeProperty(obj1,"name"));//true
            obj1.name = "new";
            console.log(hasPrototypeProperty(obj1,"name"));//false
            //for-in循环 返回所有通过对象能访问的,可枚举的(enumerated属性为true),包括实例和原型
            //!屏蔽了原型中不可枚举属性的实例属性也会被返回,根据规定所有开发人员定义的属性都是可枚举的
            for (var i in Create.prototype){//返回这个对象所有可枚举的属性~ 
                console.log(i);
            }
            //Object.keys()方法取得
            // var keys = Object.keys(Create.prototype);
            var keys = Object.keys(obj1);//实例对象与原型对象返回的是不同的!
            console.log(keys);//返回的是数组
            // var attr = Object.getOwnPropertyNames(Create.prototype);
            var attr = Object.getOwnPropertyNames(obj1);//也是不同的~  可以得到所有的实例属性 无论是否可枚举
            console.log(attr);
    
            //更简单的原型语法
            // 用一个包含所有属性和方法的对象字面量来重写整个原型对象
            function NewCreate(){};
            NewCreate.prototype = {
                // constructor: "NewCreate",//可以特意这样设置  但这样会使得这个属性变得可枚举
                name:"linbudu",
                age:"21",
                sayit:function(){
                    alert("If not me ,then who?")
                }
            }
            //注意,在这里prototype对象的constructor属性不再指向NewCreate了
            //在这里使用的语法本质上完全重写了该对象  该原型对象的construor属性指向Object构造函数
            var obj3 = new NewCreate();
            console.log(typeof obj3 );//object
            console.log(obj3 instanceof Object);//true  这些对象既是Object的实例(所有对象都继承自Object),也是NewCreate的实例
            console.log(obj3 instanceof NewCreate);
            Object.defineProperty(NewCreate.prototype,"constructor",{
                enumerable: false,
                value:NewCreate,
            })
            //原型的动态性  在原型中查找值的过程是一次搜索,因此对原型对象做的任何修改都能立即从实例上反映出来
            //即使先创建实例、后修改原型,其原因可以归结于实例与原型之间的松散连接关系,它们之间的连接只不过是一个指针而非一个副本
            //调用一个构造函数创建一个实例时,会为实例添加一个只想最初原型的[[prototype]]指针
            //如果把原型修改为另一个对象(重写),就相当于切断了构造函数与最初原型之间的联系
            function cre(){};
            var obj4 = new cre();
            cre.prototype = {
                constructor:"cc",
                name:"qqq",
                fruit:["apple","orange"]
            }
            // var obj4 = new cre();如果在重写原型之后再创建实例,那么引用的便是新的原型
            console.log(obj4.name);//undefined,obj4在重写原型之前就已经存在,它引用的仍然是最初的原型,name属性未定义
            console.log(cre.prototype.constructor);//cc  这说明重写原型对象切断了构造函数与最初原型之间的联系
            //注意 最初的原型对象依然存在,在重写前就存在的实例的[[prototype]]指针仍然指向它
    
            //牛皮的地方来了,通过原型对象不仅可以取得所有默认方法的引用,还可以定义新方法(甚至在原生的引用类型上!)
            // for(var a in String.prototype){
            //     console.log(a);
            // }
            var str = Object.keys(String);
            console.log(str);//返回一个空数组  估计是因为不可枚举
            var str1 = "qwerdfa";
            var str1_attr = Object.keys(str1);
            console.log(str1_attr);//返回0~6数组...
    
            String.prototype.newMethod = function(text){
                alert(text);
            }
            // str1.newMethod();//undefined
            // str1.newMethod(str1);//√
    
            //原型对象的问题:1.省略了为构造函数传递初始化参数,所有实例默认情况下会取得相同的属性值
            //2.对于包含基本值的属性,可以通过在实例上添加同名属性来屏蔽原型中的对应属性,达到修改值的目的
            //但对于引用类型值:见下面的例子
            var cre1 = new cre();
            var cre2 = new cre();
    
            cre1.fruit.push("pear");
            console.log(cre1.fruit);
            console.log(cre2.fruit);
            //修改了一个实例引用的数组,由于该数组存在于cre.PROTOTYPE中,修改也会通过其他的实例反映出来...
    
            //应当组合使用构造函数与原型模式
            function P(name,age,job){
                this.name = name;
                this.age = age;
                this.fruit = ["1","2","3"]
            }//在构造函数中定义实例属性
            // p.prototype = {
            //     constructor:P,
            //     shareMethod:function(){
            //         return true;
            //     }
            // }//在原型中定义共享的方法与属性
            //这样就不会发生上面的错误了,因为两个实例引用的是不同的数组
    
            // 动态原型模式
            // 在构造函数中加入
            if(typeof this.method != "function"){
                P.prototype.method = function(){
                    alert(this.attrname);
                };
            }
            //这里只有当这个方法不存在的情况下才会为原型添加这个方法
            //这段代码只有初次调用构造函数时才会执行,此后该函数的原型已经完成初始化,同时这里的修改能后立刻在所有实例中得到反映
            //不能使用对象字面量重写原型,否则会切断已有的实例与新原型之间的联系(已有实例仍会指向旧原型)
            
            //寄生构造函数模式
            //创建一个函数,仅仅用来封装创建对象的代码,再返回新创建的对象
            function parasitic(q,w,e){
                var o = new Object();
                this.q = q;
                this.w = w;
                return o;
            }
            var para_obj = new parasitic(); //与工厂模式的区别在于把函数叫做构造函数并使用new操作符
    
            //该模式可用来为对象创建特殊的构造函数 如创建带有特殊额外方法的数组 但不能直接修改Array构造函数
            function SpString(){
                // var str = new String(arguments);
                var arr = new Array();
                arr.push.apply(arr,arguments);//在arr中以传入值为参数调用push函数
                arr.newMethod = function(){
                    return this.join("|");
                };
                return arr;
                // str.newMethod = function(){
                //     return this.substring(0,3);
                // };
                // return str;
            }
            var test = new SpString("qqqqq","111","eee");
            console.log(test.newMethod());
            //构造函数返回的对象与构造函数及其原型属性之间无关(即与在构造函数的外部创建对象没有什么不同)
        </script>
    </body>
    </html>
  • 相关阅读:
    Ubuntu下录音机程序的使用
    Bash中的数学计算
    Bash中的数学扩展
    Bash的命令替换
    top的用法
    VirtualBox的快照功能
    格式化输出和printf命令
    read命令读取用户输入
    Bash的作业控制
    Codeforces Round #455 (Div. 2)
  • 原文地址:https://www.cnblogs.com/linbudu/p/10487685.html
Copyright © 2020-2023  润新知