1.對象:
1.概述:
在js中除了5中基本類型之外,剩下得都是對象Object類型(引用類型),他們的頂級父類是Object;
2.形式:
在js中,對象類型的格式為key-value形式,key表示屬性,value表示屬性的值
3.創建對象的方式:
方式1:通過new關鍵字創建(不常用)
let person = new Object();// 添加屬性 與 值person.name="張三";person.age = 22;console.log(person)
方式2:通過{}類似于JSON字符串的形式創建:
let person = {name: '張三',age: 20}console.log(person);
4.對象的相關操作:
1.添加屬性:
let person = new Object();// 添加屬性 與 值person.name="張三";person.age = 22;console.log(person)
let person = {name: '張三',age: 20}console.log(person);// 添加屬性person.sex = '男';
2.獲取屬性值:
對于new方式創建的對象,獲取屬性值的方式如下:
let person = new Object();// 添加屬性 與 值person.name="張三";person.age = 22;console.log(person.name)
對于{}方式創建的對象,獲取屬性值得方式如下(上面得方式也適用):
let person = {name: '張三',age: 20}console.log(person['age']);
3.刪除屬性:通過delete關鍵字實現(兩種創建方式都適用)
let person = {name: '張三',age: 20,sex:'男'}delete person.sex;console.log(person);
4.遍歷對象屬性:for-in循環
let person = {name: '張三',age: 20,sex:'男'}for (let personKey in person) {console.log(personKey,person[personKey]);}
2.函數:不依賴于實例對象
1.概述:
是由一連串的子程序(語句的集合)組成的,可以被 外部調用,向函數內部傳入參數之后,函數可以返回的一定的值得代碼集合;
2.函數對象的創建:
方式1;通過new關鍵字:(不常用)
let 函數名=new Function("執行的語句");
let funA = new Function("console.log('函數對象執行了')");
//調用函數:
funA();
方式2:聲明式創建函數
function 函數名(形參1,形參2.....) {語句}
function sum(num1, num2, num3) {console.log("執行了sum函數:收到了參數:", num1, num2, num3)//可以使用return 返回結果return num1 + num2;}//調用函數let ret=sum(1,2);console.log(ret);
注意事項:
-
調用有參函數時,傳入的參數不受函數定義的函數列表影響,可以多傳,少穿,或者不傳;
-
創建函數時,不涉及返回值(類似于Java中的構造方法),但函數體內可以return執行結果;
3.函數的類型:
1.常規函數:上述函數即為常規函數
function 函數名(形參1,形參2.....) {語句}
2.匿名函數:沒有函數名,而是由一個變量進行接收
let 變量名(函數名)=function(形參1,形參2.....) {執行語句}
3.嵌套函數:即函數體內包含一個子函數
function 父函數名(形參1,形參2.....) {function 子函數名(形參1,形參2.....) {語句} }
注意:直接調用父函數時,無法執行子函數
function fu() {function zi() {console.log("我是子函數");}父函數的其他執行語句} //調用父函數fu();
如果需要執行子函數,則需要在父函數中手動調用子函數
function fu() {function zi() {console.log("我是子函數");}zi();// 父函數的其他執行語句} //調用父函數fu();
4.立即執行函數:可理解為函數一創建出來就被調用執行
(function (形參1,.....) {執行語句})(實參.........)
(function (msg) {console.log("我是一個匿名函數",msg)})('我是一段消息');
3.方法:需依賴于示例對象
1.方法的定義:
需要先創建對象,然后依賴對象在創建方法;
let person = {name:'張三',age:23,//定義方法sayHello:function () {console.log(this.name+",Hello")}}console.log(person)//調用方法person.sayHello();
2.this關鍵字:
1.this出現在函數中:被直接調用,則this表示window對象
<script>function fun() {console.log(this.constructor+ ",Hello")}fun();</script>
通過輸出的構造方法名,可以看出此時的this表示window對象
2.this出現在方法中:this表示當前對象(誰調用,this就指代誰)
function fun() {console.log(this.name + ",Hello")}let person = {name: '張三',age: 23,sayHello: fun}let person2 = {name: '李四',age: 23,sayHello: fun}person.sayHello(); //對象調用方法person2.sayHello();
通過測試結果可以看出,在方法中的this被那個對象調用,this就指代那個對象
4.創建對象的幾種方式:
1.直接創建:
存在問題:
如果需要創建的對象過多,直接創建無法實現代碼的高復用性,代碼冗余嚴重;
2.通過工廠模式,封裝創建對象的方法:
實現:
function createPerson(name, age) {let obj = new Object();obj.name = name;obj.age = age;obj.sayHello = function () {console.log(this.name)}return obj;}let person1 = createPerson('張三', 22); //Object 類型let person2 = createPerson('李四', 22); //Object 類型console.log(person1,person2)
存在問題:
通過下面測試結果可以看出,使用工廠模式創建的對象沒有獨立的類型,全部都是Object;
function createPerson(name, age) {let obj = new Object();obj.name = name;obj.age = age;obj.sayHello = function () {console.log(this.name)}return obj;}let person1 = createPerson('張三', 22); //Object 類型let person2 = createPerson('李四', 22); //Object 類型console.log(typeof person1, typeof person2)
3.通過構造函數創建對象:
構造函數是什么:
-
構造函數就是一個普通的函數,創建方式和普通函數沒有區別;
-
不同的是 構造函數一般首字母大寫,調用時不同 需要使用new關鍵字;
構造函數的執行流程:
-
1.調用構造函數,會立刻創建一個新的對象
-
2.將新建的對象設置為函數中的this,在構造函數中可以使用this開引用新建的對象
-
3.逐行執行函數中的代碼
-
4.將新建的對象返回
實現通過構造函數創建對象:
function Person(name, age) {this.name = name;this.age = age;this.sayName = function () {console.log(this.name)}}let person1 = new Person('張三', 23);let person2 = new Person('李四', 23);console.log(person1, person2)
說明:
通過此種方式創建的對象,都有獨立的類型,不再是object,而是與構造函數有關
function Person(name, age) {this.name = name;this.age = age;this.sayName = function () {console.log(this.name)}}let person1 = new Person('張三', 23);let person2 = new Person('李四', 23);console.log(person1, person2) function Person2(name, age) {this.name = name;this.age = age;this.sayName = function () {console.log(this.name)}}let person3 = new Person2('李四', 23); // 判斷 person3 是 Person2 / Person 創建的console.log(person3 instanceof Person)console.log(person2 instanceof Person)console.log(person3 instanceof Person2)
由于person3是通過Person2構造函數創建的,所以 console.log(person3 instanceof Person)輸出為false,console.log(person3 instanceof Person2)輸出為true;
5.原型:
1.概述:
-
我們創建的每一個函數,解析器都會向函數中添加一個屬性prototype,這個屬性對應著一個對象,這個對象就是我們所謂的原型對象,即顯式原型,
-
原型對象就相當于一個公共的區域,所有同一個類的實例都可以訪問到這個原型對象,我們可以將對象中共有的內容,統一設置到原型對象中。
-
普通函數調用prototype沒有任何作用,當函數以構造函數的形式調用時,它所創建的對象中都會有一個隱含的屬性,指向該構造函數的原型對象,我們可以通過__proto__(隱式原型)來訪問該屬性。
2.案例解析:
//創建構造函數function Person(name, age) {this.name = name;this.age = age;this.sayName = function () {console.log(this.name)}}//將屬性 或者 函數 存在原型中 共享的Person.prototype.xxx = "我是測試數據";Person.prototype.showInfo = function () {console.log(this.name, "我是原型中的方法")} let person1 = new Person('張三', 23);let person2 = new Person('李四', 23);console.log(person1, person2);
//創建構造函數function Person(name, age) {this.name = name;this.age = age;this.sayName = function () {console.log(this.name)}}//將屬性 或者 函數 存在原型中 共享的Person.prototype.xxx = "我是測試數據";Person.prototype.showInfo = function () {console.log(this.name, "我是原型中的方法")} let person1 = new Person('張三', 23);let person2 = new Person('李四', 23);//person1 訪問xxx 先從person1對象自身尋找xxx屬性//沒有的 就去原型中找// 在沒有 就通過父類找console.log(person1.xxx, person2.xxx)person2.showInfo();

因為在Person類的原型對象中添加了屬性xxx和showinfo方法,而person1和person2作為它的實例對象,在訪問時,由于從自身無法獲取到,就向上在共享的原型對象中訪問到了屬性xxx和方法showinfo;
//創建構造函數function Person(name, age) {this.name = name;this.age = age;this.sayName = function () {console.log(this.name)}}//將屬性 或者 函數 存在原型中 共享的Person.prototype.xxx = "我是測試數據";Person.prototype.showInfo = function () {console.log(this.name, "我是原型中的方法")} let person1 = new Person('張三', 23);let person2 = new Person('李四', 23);//使用 in 檢查對象中是否 含有某個屬性 有 true 沒有 falseconsole.log('name' in person1);console.log('xxx' in person1);console.log(person1.hasOwnProperty('name'))//true xxx不是person1 自身的數據console.log(person1.hasOwnProperty('xxx'))console.log(person1.__proto__.hasOwnProperty('xxx'))
由于xxx屬性是原型對象中的屬性,而不是person1自身的屬性,所以測試結果為false;
6.繼承:
1.方式:
-
** * 1.原型鏈繼承**
-
** * 2.構造方法繼承**
-
** * 3.組合繼承**
-
** * 4.原型式繼承**
-
** * 5.寄生繼承**
-
** * 6.寄生組合繼承**
2.原型鏈繼承:
實現:
function SupperType() {this.supProp = "我是父類型中的屬性"}//給父類的原型添加方法SupperType.prototype.showSupperProp = function () {console.log(this.supProp);}//創建子類原型function SubType() {this.subType = "我是子類的屬性"}//繼承 讓子類的原型屬性 指向 父類類型SubType.prototype = new SupperType();//將子類的原型的構造方法屬性設置為子類自己SubType.prototype.constructor = SubType;//子類的原型對象添加方法SubType.prototype.showSubProp = function () {console.log(this.subType)}let subType = new SubType();//調用父類的原型中的方法subType.showSupperProp();//調用子類自己的原型中的方法subType.showSubProp();//獲取父類中的屬性console.log(subType.supProp)console.log(subType)
說明:
上述代碼中是通過將子類的原型對象指向父類對象來實現的,在子類訪問父類屬性或方法時,先在自身找,如果找不到,再在子類的原型對象中找,要是還找不到,就在父類對象中找,父類對象屬性中也沒有,就在父類的原型對象中找,就這樣以引用鏈的形式查找,進而實現了繼承;
存在問題:
-
1.不能為父類傳參;
-
2.原型鏈繼承多個實例的引用類型屬性,且由于指向是相同的,一個實例修改了原型屬性,另一個實例的原型屬性也會被影響;
3.構造函數繼承:
實現:
//1.定義父類的構造函數function SupperType(name) {this.name = name;this.showSupperName = function (){console.log(this.name,"這是方法")}}SupperType.prototype.xxx= "父類原型屬性";//2.創建子類構造函數function SubType(name,age) {//在子類中 調用call 繼承父類中的屬性與方法SupperType.call(this,name);this.age = age;}SubType.prototype.showInfo = function () {console.log(this.name,this.age)}let subType = new SubType('張三',20);subType.showSupperName();//子類原型中的方法subType.showInfo();//通過子類實例 調用父類原型中的屬性console.log(subType.xxx);console.log(subType)
說明:
通過此方式實現繼承與原型鏈的方式不同,此方式更像是把父類的屬性和方法復制到子類中,雖然最后看似在調用父類的屬性和方法,但其實是調用本類的屬性和方法,因此通過此方式并沒有建立完全的繼承關系,所以subType的父類是Object,而非SupperType,因此子類SubType是無法訪問到父類SupperType的原型對象中的屬性和方法的;
存在問題:
通過構造函數實現繼承關系,解決了原型鏈繼承的問題.但又出現了下面的新問題:
-
無法訪問父類原型對象中的方法或屬性;
4.組合繼承:
實現:
<script>//1.定義父類的構造函數function SupperType(name) {this.name = name;this.showSupperName = function (){console.log(this.name,"這是父類方法")}}SupperType.prototype.xxx= "父類原型屬性";//2.創建子類構造函數function SubType(name,age) {//在子類中 調用call 繼承父類中的屬性與方法SupperType.call(this,name);this.age = age;}SubType.prototype=Object.create(new SupperType());SubType.prototype.constructor=SubType;SubType.prototype.showinfo=function(){console.log(this.name,this.age,"子類方法")}let sub=new SubType("張三",22);sub.showSupperName();sub.showinfo();console.log(sub.xxx);console.log(sub);
</script>
說明:
此方式結合了引用鏈繼承和構造方法繼承的特點,解決了它們自身存在的問題
存在問題:
雖然組合繼承解決了引用鏈繼承和構造方法繼承所存在的問題,但有出現了新的問題;
-
父類中的實例屬性和方法在子類實例中又在子類原型中,內存開銷變大;