一,構造函數,原型對象,實例的關系:
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
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...in或Object.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.存取器描述(注意:當使用了getter或setter方法,不允許使用writable和value這兩個屬性)
當使用存取器描述屬性的特性的時候,允許設置以下特性屬性:
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
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
使用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