原型鏈
__proto__ 和 prototype
定理:
- 所有的構造器對象都是由Function創建出來的(包括Function自己)
Object Boolean String Number Function Array RegExp Date Error
以上是構造器對象都是Function的實例
- 實例對象擁__proto__屬性,對象(類)擁有prototype, 如果同時擁有這2個屬性,證明它是Function的實例對象也是對象(類),反過來說如果是Function的實例對象就會有__proto__和prototype屬性
Object Boolean String Number Function Array RegExp Date Error
以上是構造器對象都是Function的實例也都會有__proto__和prototype屬性
- 所有的實例對象的__proto__屬性都指向,該對象(類)的prototype
通過這個結論得知:Object Boolean String Number Function Array RegExp Date Error
的__proto__都是相等的,因為他們都是Function實例指向Function.prototype
既:
Object.__proto__ === Boolean.__proto__ ===
String.__proto__ === Number.__proto__ ===
Function.__proto__ === Array.__proto__ ===
RegExp.__proto__ === Date.__proto__ === Error.__proto__
特殊的Function
Function.__proto__ === Function.prototype 因為Function是Function創建的所以根據定理3得出此條
4.constructor指向該實例對象的構造對象
Object.constructor === Function
Object.constructor.__proto__ === Function.__proto__
Object.constructor.prototype === Function.prototype
Object.constructor.__proto__ === Function.prototype
Object.constructor.prototype === Function.__proto__
Function.__proto__ === Function.prototype
復合繼承
- call繼承屬性
- 原型繼承方法
- 改變constructor(構造器)的指向
// 定義類型
function Ponse(name,age){
// 定義屬性
this.name = name;
this.age = age;
};
// 定義方法
Ponse.prototype.getName = function(){
return this.name;
}
// 定義方法
Ponse.prototype.getAge = function(){
return this.age;
}
// 定義類型
function Student(name,age,number){
// 屬性繼承
// 用call改變Ponse的this指向當前創建的對象,將Ponse的屬性掛載到當前對象上
Ponse.call(this,name,age);
this.number = number;
}
// 方法繼承, 繼承getName和getAge方法,將Student.prototype指向ponse的實例就可以擁有ponse的方法
Student.prototype = new Ponse();
Student.prototype.getNumber = function(){
return this.number;
}
// 上面將prototype指向了Ponse的實例對象,那么Student.prototype.constructor(構造器)就是Ponse了
// 將構造器更改為Student, 否則創建出來的對象的構造函數式Ponse
Student.prototype.constructor = Student;
var zhangfei = new Student("張飛","25","0513");
console.log(zhangfei.getName(),zhangfei.getAge(),zhangfei.getNumber(),zhangfei.constructor);
變量提升函數提升
- 變量提升,在代碼運行之前會先變量提升給變量賦初始值undefined,
console.log(name) // 報錯
// VM969:1 Uncaught ReferenceError: nameis not defined
console.log(name); // 此時會輸出undefined 證明變量已經存在但未賦值
var name = "張飛";
上面代碼執行時就等于
var name;
console.log(name); // 此時會輸出undefined 證明變量已經存在但未賦值(初始值是undefined)
name = "張飛";
- 函數提升,在代碼運行之前會函數提升
fun(); // 會正常運行 彈出"張飛"
function fun(){
alert("張飛");
}
上面代碼執行時就等于
function fun(){
alert("張飛");
}
fun(); // 會正常運行 彈出"張飛"
- 變量提升和函數提升先執行哪個呢? 先變量提升在函數提升
fun() // 此時會正常輸出“張飛”
function fun(){
alert("張飛");
}
var fun = 123;
上面代碼運行時如下
var fun;
function fun(){
alert("張飛");
}
fun();
fun = 123;
思考:
fun();
var fun = function(){
alert("張飛");
}
// 執行結果是fun is not a function 說fun不是一個函數, 這里用的是var所以是變量提升
// 變量提升的特征是先聲明變量同時初始化變量為undefined, 所以調用失敗
// 上面的代碼在運行時是這樣的
var fun;
fun();
fun = function(){
alert("張飛");
}
var name = "張飛";
function fun(){
alert(name);
var name = "關羽";
}
fun(); // 會彈出"undefined"
// 運行時代碼如下
var name; // 全局變量提升
function fun(){
var name; // 局部變量提升
alert(name);
name = "關羽"; // 局部變量賦值
}
name = "張飛"; // 全局變量賦值
fun();
function fun(){
fun2 = function (){
alert("fun2");
}
}
fun();
fun2(); // 會打印“fun2”
// 運行時代碼如下
var fun2;
function fun(){
fun2 = function (){
alert("fun2");
}
}
fun(); // 執行給fun2賦值
fun2(); // 執行fun2
// 那么我們這樣寫會怎么樣
function fun(){
var fun2 = function (){
alert("fun2");
}
}
fun();
console.log(fun2); // 報錯
fun2(); // 報錯
// 因為fun2是 fun的局部變量外界訪問不到