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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM