zoukankan      html  css  js  c++  java
  • JS原型对象

    一,构造函数,原型对象,实例的关系:

    1,构造函数实例:封装的函数,如果通过new操作符来调用的,就是构造函数,如果没有通过new操作符来调用的,就是普通函数

    var person1 = new Person('Mike',10); // 此时的person1 称为person的一个实例,而person就是构造函数了

    2,函数Person(对象)有个属性prototype(指针)指向原型对象。 Person.prototype(原型对象,实质也是对象),他有个属性constructor(指针) ,又指向 Person函数对象

    console.log(Person === Person.prototype.constructor); //true

    3,实例对象person1有个属性[prototype](内部属性,chrome和firefix,Safari,中这个属性叫_proto_)指向原型对象。实例对象可以通过这个属性访问原型对象上的属性和方法(Perons.prototype._proto_ == person1._proto_)。

    //举例:
     function Person(name, age) {
        this.name = name;
        this.age = age;
    }
    //在原型对象中添加属性或者方法
    Person.prototype.sex = '男'; 
    var person1 = new Person('Mike',10); 
    var person2 = new Person('Alice',20);
    //只给person2设置性别
    person2.sex = '女';
    console.log(person1.sex)  // '男'
    console.log(person2.sex)  //'女' 
    //解释:这里我们没有给person1实例设置sex属性,但是因为[Protoptype]的存在,会访问原型对象中对应的属性;同时我们给person2设置sex属性后输出的是'女',说明只有当实例本身不存在对应的属性或方法时,才会去找原型对象上的对应属性或方法

    4,可以通过实例对象(person1)的constructor(person1.constructor)访问构造函数,但是constructor本质上是原型对象的属性。

    二,继承:

    1,继承的主要思路是利用原型链,而原型链的原理是:让一个引用类型继承另一个引用类型的属性和方法。即原型对象通过constructor指向构造函数,实例对象通过_ptoto_指向原型对象。

    function A() {}
    
    //在A的原型上绑定sayA()方法
    
    A.prototype.sayA = function(){
    
        console.log("from A")
    
    }
    
    function B(){}
    
    //让B的原型对象指向A的一个实例
    
    B.prototype = new A();
    
    //在B的原型上绑定sayB()方法
    
    B.prototype.sayB = function(){
    
        console.log("from B")
    
    }
    
    //生成一个B的实例
    
    var a1 = new A();
    
    var b1 = new B();
    
    //b1可以调用sayB和sayA
    
    b1.sayB();//'from B'
    
    b1.sayA();//'from A'

    说明:2-1,最后我们调用了b1的sayB方法,可以执行,为什么?

    因为b1有[Prototype]属性可以访问B prototype里面的方法;

    2-2,我们调用了b1的sayA方法,可以执行,为什么?

    因为b1沿着[Prototype]属性可以访问B prototype,B prototype继续沿着[Prototype]属性访问A prototype,最终在A.prototype上找到了sayA()方法,所以可以执行

    3,JavaScript语言的传统方法是通过构造函数,定义并生成新对象,prototype 属性使您有能力向对象添加属性和方法。下面是通过传统的方式创建和使用对象的案例:

    //Person.js文件
    function Person(x,y){ this.x = x; this.y = y; }
    Person.prototype.name = 'sky';  //原型对象上的属性 
    Person.prototype.toString = function (){ //原型对象上的方法 

      return (this.x + "的年龄是" +this.y+"岁");

    }
    export {Person};

    //index.js文件

    import {Person} from
    './Person';

    let person
    = new Person('张三',12);

    console.log(person.toString());

     

    4, 代码中,对象p的的name属性为私有变量,使用p.name不能直接访问

    5,以上代码ES6 类class写法:

    //Person.js
    
    class Person{
        // 构造
        constructor(x,y){  //构造方法
            this.x = x;
            this.y = y;
        }   //方法之间不需要逗号分隔,加了会报错
        toString(){  //原型对象上的方法
            return (this.x + "的年龄是" +this.y+"岁");
        }
    }
    
    export {Person};
    
    //index.js
    
    import {Person} from './Person';
    
    let person = new Person('张三',12);
    
    console.log(person.toString());

    constructor方法是类的构造函数是默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor方法,如果没有显式定义,一个默认的constructor方法会被添加。所以即使你没有添加构造函数,也是有默认的构造函数的。一般constructor方法默认返回实例对象this。类的方法内部如果含有this,它默认指向类的实例。。(个人理解:

    Person.prototype = this = person1

    Person.prototype.constructor = Person,

    person1.constructor == Person

    person1.__proto__ === person1.constructor.prototype)

    6,下面 这个类的名字是Expression而不是Expre,Expre只在Class的内部代码可用,指代当前类

    const Expression = class Expre{
    
        static getAge(){
    
            return '12';
    
        }
    
        getClassName(){
    
            return " ClassName1= " +Expre.name + " ClassName2= " +Expression.name;
    
        }
    
    };
    
    let exp = new Expression();
    
    console.log(exp.getClassName());//ClassName1= Expre ClassName2= Expre
    
    console.log(Expression.getAge());

    说明:Expre.name和Expression.name返回的都是Expre,返回的都是当前类名

    如果类的内部没用到的话,可以省略Expre,也就是可以写成下面的形式:

    const MyExpre = class{
    
       getClassName(){
    
            return MyExpre.name;
    
    }
    
    }
    
    let myExpre = new MyExpre();
    
    console.log(myExpre.getClassName());//MyExpre

    6,立即执行class:

    let person = new class{
    
        // 构造
    
        constructor(props) {
    
            this.props = props;
    
        }
    
        getProps(){
    
            return this.props;
    
        }
    
    }('构造函数的参数');

    console.log(person.getProps());

    //构造函数的参数类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。

    7, 二次说明:

    var o1 = {};

    var o2 =new Object();

    var o3 = new f1();

    function f1(){};

    var f2 = function(){};

    var f3 = new Function('str','console.log(str)');

    console.log(typeof Object); //function

    console.log(typeof Function); //function  

    console.log(typeof f1); //function

    console.log(typeof f2); //function

    console.log(typeof f3); //function   

    console.log(typeof o1); //object

    console.log(typeof o2); //object

    console.log(typeof o3); //object

    每个对象都有 __proto__ 属性,但只有函数对象才有 prototype 属性

    原型对象(Person.prototype)是 构造函数(Person)的一个实例

    构造器

    var obj = {}

    它等同于下面这样:

    var obj = new Object()

    obj 是构造函数(Object)的一个实例。所以:

    obj.constructor === Object

    obj.__proto__ === Object.prototype

    同理:

    var b = new Array();
    b.constructor === Array;
    b.__proto__ === Array.prototype;
    var c = new Date();
    c.constructor === Date; 
    c.__proto__ === Date.prototype; 
    var d = new Function(); 
    d.constructor === Function; 
    d.__proto__ === Function.prototype;

    试题:

    1,因为 Person的构造函数 === Function

    所以 Person.__proto__ === Function.prototype

    2,Person.prototype 是一个普通对象,我们无需关注它有哪些属性,因为一个普通对象的构造函数 === Object

    所以 Person.prototype.__proto__ === Object.prototype

    2,Object.prototype 对象也有proto属性,但它比较特殊,为 null 。因为 null 处于原型链的顶端,这个只能记住。

    Object.prototype.__proto__ === null

    Function.prototype.__proto__ === Object.prototype

    所有函数对象proto都指向Function.prototype,它是一个空函数(Empty function)

    所有对象的 __proto__ 都指向其构造器的 prototype

    Boolean.__proto__ === Function.prototype // true Boolean.constructor == Function //true 
    String.__proto__ === Function.prototype // true 
    String.constructor == Function //true 
    // 所有的构造器都来自于Function.prototype,甚至包括根构造器Object及Function自身 
    Object.__proto__ === Function.prototype // true Object.constructor == Function // true 
    // 所有的构造器都来自于Function.prototype,甚至包括根构造器Object及Function自身 Function.__proto__ === Function.prototype // true 
    Function.constructor == Function //true 
    Array.__proto__ === Function.prototype // true 
    Array.constructor == Function //true 
    RegExp.__proto__ === Function.prototype // true 
    RegExp.constructor == Function //true 
    Error.__proto__ === Function.prototype // true 
    Error.constructor == Function //true 
    Date.__proto__ === Function.prototype // true 
    Date.constructor == Function //true
    View Code
    Number.__proto__ === Function.prototype // true Number.constructor == Function //true
    Math.__proto__ === Object.prototype // true 
    Math.construrctor == Object // true
    JSON.__proto__ === Object.prototype // true
    JSON.construrctor == Object //true

    // 上面的函数对象自定义的,

    函数声明 function Person() {} // 函数表达式 var Perosn = function() {} console.log(Person.__proto__ === Function.prototype) // true

    Array.prototype 继承了对象的所有方法,当你用Array.hasOwnPrototype()时,JS 会先查一下它的构造函数 (Array 的原型对象 Array.prototype 有没有有hasOwnPrototype()方法,没查到的话继续查一下 Array.prototype 的原型对象 Array.prototype.__proto__ === Object.prototype有没有这个方法

    function Person(name) { this.name = name } // 修改原型 Person.prototype.getName = function() {} var p = new Person('jack') console.log(p.__proto__ === Person.prototype) // true console.log(p.__proto__ === p.constructor.prototype) // true

    -----------------------------------------

    function Person(name) { this.name = name } // 重写原型 Person.prototype = { getName: function() {} } var p = new Person('jack') console.log(p.__proto__ === Person.prototype) // true console.log(p.__proto__ === p.constructor.prototype) // false

    这也很好理解,给Person.prototype赋值的是一个对象直接量{getName: function(){}},使用对象直接量方式定义的对象其构造器(constructor)指向的是根构造器Object,Object.prototype是一个空对象{},{}自然与{getName: function(){}}不等。

    1,对于私有属性,我们是不可以直接通过 Class 实例来引用的,这也是私有属性的本来含义。但是有一种情况除外,在 Class 定义中,我们可以引用 Class 实例的私有属性:

    class Foo { 
        #privateValue = 42; 
        static getPrivateValue(foo) { 
          return foo.#privateValue; 
        } 
    } 
    Foo.getPrivateValue(new Foo()); // >> 42

    1,数据描述,Object.defineProperty(obj, prop, descriptor) <在ie8下只能在DOM对象上使用>。

    obj:必需。目标对象 

    prop:必需。需定义或修改的属性的名字

    descriptor:必需。目标属性所拥有的特性-->{

    configurable: false, //是否可以删除目标属性或是否可以再次修改属性的特性(writable, configurable, enumerable

    enumerable:false, //是否可以被枚举(使用for...inObject.keys()

    value:任意类型的值, //prop key对应的值

    writable:false //是否可以被重写

    }

    以上默认值全为false

    var obj = {}
    
    Object.defineProperty(obj,"newKey",{ value:"hello", writable:false, enumerable:false, configurable:true }); //删除属性 delete obj.newKey; console.log( obj.newKey ); //undefined

    2.存取器描述(注意:当使用了gettersetter方法,不允许使用writablevalue这两个属性)

    当使用存取器描述属性的特性的时候,允许设置以下特性属性:

    var obj = {};
    
    Object.defineProperty(obj,"newKey",{
    
        get:function (){} | undefined,
    
        set:function (value){} | undefined
    
        configurable: true | false
    
        enumerable: true | false
    
    });
    
    var obj = {};
    
    var initValue = 'hello';
    
    Object.defineProperty(obj,"newKey",{
    
        get:function (){
    
        //当获取值的时候触发的函数
    
        return initValue;
    
    },
    
    set:function (value){
    
        //当设置值的时候触发的函数,设置的新值通过参数value拿到
    
        initValue = value;
    
    }
    
    });
    
    //获取值
    
    console.log( obj.newKey ); //hello
    
    //设置值
    
    obj.newKey = 'change value';
    
    console.log( obj.newKey ); //change value

    3, 使用立即执行函数来实现模块化,所谓模块化,就是根据需要控制模块内属性与方法的可访问性,即私有或者公开。在代码中,module为一个独立的模块,N为其私有属性,print为其私有方法,return 里的decription为其公有属性,add为其共有方法。

     

    4,变量提升,变量声明被提升了,而变量赋值不会被提升。即被定义了但值为undefined

    5,柯里化,即Currying,可以是函数变得更加灵活。我们可以一次性传入多个参数调用它;也可以只传入一部分参数来调用它,让它返回一个函数去处理剩下的参数。

                          var currying = function(x){
                    return function(y){
                        return x + y;
                    }
                }
                console.log(currying(1))    //ƒ (y){eturn x + y;}
                console.log(currying(1)(1))    //2
                var cc = currying(1)
                console.log(cc(1))    //2
                console.log(cc(11))    //12  
    View Code

    6,apply方法使用数组指定参数,而call方法每个参数单独需要指定:

    • apply(thisArg, [argsArray])
    • call(thisArg, arg1, arg2, …)
    • this.Arg表示指向的对象,arg1表示_proto_的方法传的形参
                          var user = {
                    name : 'qinhuansky',
                    say : function(yaya){
                        console.log(this.name +'-'+ yaya)
                    }
                }
                user.say()    //qinhuansky-undefined
                var user2 = {
                    name: "qin"
                }
                user.say.call(user2, 'haha')    //qin-haha
    View Code

     

    使用bind方法,可以为函数绑定this值,然后作为一个新的函数返回:

    var user = {
      greet: "Hello!",
      greetUser: function(username){
        return this.greet+" "+ username;
      }
    }
    
    console.log(user.greetUser("aa"))
    var fun = user.greetUser.bind(user);
    var fun1 = user.greetUser.bind({greet: "qin"})
    console.log(fun("bb"))
    console.log(fun1("chuan"))
    console.log('---------------------------------')
    var user2 = {
        greet : 'Hello2'
    }
    console.log(user.greetUser.call(user2))
    console.log(user.greetUser.call(user2, 'dd'))
    console.log(user.greetUser.apply(user2, ['ee']))
    > Hello! aa
    > Hello! bb
    > qin chuan
    > ---------------------------------
    > Hello2 undefined
    > Hello2 dd
    > Hello2 ee

    函数声明可以先调用再声明,而函数表达式必须先定义再调用

    tip:

    1,常量大小字母;注释只有在写业务逻辑判断才写

    2,词法环境:通过function声明的函数和var 定义的变量,都会有个window预处理阶段,提前调用为,对一个函数的引用和undefuned

    参考简书:https://www.jianshu.com/p/dee9f8b14771

    本想把日子过成诗,时而简单,时而精致,不料日子却过成了一首歌,时而不靠谱,时而不着调
  • 相关阅读:
    Codeforces Round #548
    省选前的th题
    省选前多项式的挣扎
    2019.3.18考试&2019.3.19考试&2019.3.21考试
    省选前的反演抢救计划
    2019.3.16 noiac的原题模拟赛
    AtCoder Regular Contest 069 F
    Atcoder Grand 012 C
    Atcoder Grand 011 C
    Atcoder Grand 006 C-Rabbit Exercise
  • 原文地址:https://www.cnblogs.com/chuanq/p/11016509.html
Copyright © 2011-2022 走看看