JavaScript面向對象編程詳解

      網友投稿 614 2025-04-04

      面向對象編程

      在學習了js高級以及es6之后,再來學習面向對象編程,之前在學習es6,canvas的時候已經有接觸到了這種思想,感覺還是需要在深入的學習一下,這部分內容牽扯到很多原型鏈部分的東西,當做復習一下還是很不錯的!

      ES5中的面向對象

      面向對象編程(OOP)具有靈活、代碼可復用、高度模塊化等特點。

      對象是單個實物的抽象

      對象是一個容器,封裝了對應的屬性和方法,屬性是對象的狀態,方法是對象的行為(完成的任務)

      構造函數實例化對象

      在ES5中沒有class類的概念,所以面向對象是基于構造函數和原型鏈的,

      注意:構造函數的名字的第一個字母要大寫

      特點

      函數體內使用this關鍵字,代表了所要生成的對象實例

      生成對象,必須使用new關鍵字實例化

      function Dog() { this.name = name; this.age = age; } var dog1 = new Dog('dw', 10)

      如果沒有使用new關鍵字,則結果會是undefind,原因是該函數沒有返回值

      instanceof 運算符用于檢測構造函數的 prototype 屬性是否出現在某個實例對象的原型鏈上。返回trueorfalse

      通過instanceof來判斷當前的的對象是否是實例化出來的,如果是實例化出來的this指向實例化出來的對象,也就是這里的Person,否則作為普通函數來說當前的this指向window

      function Person(name, age){ if(this instanceof Person){ // this指向了當前的實例,外部使用了關鍵字new this.name = name; this.age = age; }else{ // this指向了window,外部沒有使用了關鍵字new return new Person(name,age); }

      創建一個空對象

      構造函數的this,繼承函數原型

      讓this指向構造函數的對象實例,執行構造函數內容為新對象添加屬性和方法

      返回這個對象

      var obj = {}//創建空對象 obj.__proto__ = Person.prototype;//繼承作用域 Person.call(obj,)//改變this指向 return obj //返回對象

      每個對象在創建時都會自動擁有一個構造函數屬性constructor

      constructor是對象__proto__上的一個屬性(如果該對象是函數,則在其prototype上),通常指向生成這個對象的函數,也就是指向構造函數的引用

      obj.constructor === Obj

      代碼冗余

      能夠共享內部的屬性和方法

      原型對象

      function Foo(){}; var foo = new Foo();

      原型對象:Foo.prototype

      實例對象: foo就是實例對象,每一個原型對象中都有一個__proto__,每個實例對象都有一個constructor屬性,這個constructor通過繼承關系繼承來的,它指向了當前的構造函數Foo

      構造函數:用來初始化新創建對象的函數,Foo是構造函數,自動給構造函數賦予一個屬性prototype,該屬性指向了實例對象的原型對象

      function Foo(){} var foo = new Foo()

      繼承機制:通過原型對象(prototype)實現繼承

      原型對象的作用,就是定義所有實例對象共享的屬性和方法

      Foo.prototype.name = 'ljc';

      tips:所有的對象都有自己的原型對象

      原型鏈:對象的原型 => 原型的原型 => 原型的原型的原型 => null

      所有的對象都繼承了Object.prototype上的屬性和方法

      查找屬性和方法的規則:js引擎會先尋找對象本身的屬性和方法,如果找不到就到它的原型對象去找,如果還是找不到,就到原型的原型去找,如果直到最頂層的Object.prototype還是找不到,就會返回undefined

      constructor屬性注意點:一旦我們修改構造函數的原型對象,為防止引用出現問題,同時也要修改原型對象的constructor屬性

      function MyArray(){};//構造函數 MyArray.prototype = Array.prototype;//復制數組的原型對象,獲得方法 MyArray.prototype.constructor = MyArray;//改變constructor指向 var arr = new MyArray();//實例化對象

      創建對象的n種方式

      字面量

      var Person = { name: 'ljc' }

      new關鍵字

      var obj = new Object(); obj.name = 'ljc'; console.log(obj);

      Object.create(對象)

      var a = { getName:function(){ console.log('ljc'); } } var b = Object.create(a); b.getName();

      缺點:代碼冗余

      優點:能夠創建多個類似的對象

      function createObj(name,age) { var o = new Object();//創建對象 //添加屬性和方法 o.name = name; o.age = age; o.sayName = function() { console.log(this.name); } //返回對象 return o } var obj = createObj('ljc',19)//每次調用返回一個對象

      缺點:所有的constructor指向都相同,沒有解決對象識別問題

      function Person(name,age){ this.name = name; this.age = age; this.sayName = function(){ console.log(this.name); } } var man = new Person('ljc',19); var woman = new Person('dw',18);

      通過在外部使用new關鍵字,將屬性和方法通過this綁定到相應的對象上,解決了工廠模式的遺留問題

      缺點:每個對象都會有一個sayName方法,執行的功能是一樣的,但是卻仍然占用了不同的內存空間,浪費了內存資源

      構造函數擴展模式

      function Person(name,age){ this.name = name; this.age = age; this.sayName = sayName; } function sayName(){ console.log(this.name); }

      將sayName方法定義成全局函數,解決了內存浪費的問題

      缺點:污染全局空間

      寄生構造函數模式

      function createObj(name,age) { var o = new Object();//創建對象 //添加屬性和方法 o.name = name; o.age = age; o.sayName = function() { console.log(this.name); } //返回對象 return o } var obj = new createObj('ljc',19)

      結合了工廠模式和構造函數模式:創建一個函數,函數體內部實例化一個對象,并且將對象返回,在外部使用new來實例化對象

      缺點:由于沒有使用構造函數,所以instanceof運算符和prototype屬性沒有意義

      穩妥構造函數模式

      function Person(name){ var a = 10; var obj = new Object(); obj.sayName = function(){ console.log(a); console.log(name); } return o; } var p1 = Person('ljc'); p1.sayName();

      沒有公共屬性,并且它的方法也不引用this,name屬于函數內部的私有屬性,有點像閉包,p1被稱為穩妥對象

      缺點:由于沒有使用構造函數,所以instanceof運算符和prototype屬性沒有意義

      將屬性方法綁定在prototype上,實現共享

      注意:需要改變constructor指向

      function Person(){} Person.prototype = { constructor: Person, name: 'ljc', age: 19, friends:['dw','xy'], sayName:function(){ console.log(this.name); } } var me = new Person(); var you = new Person();

      每個通過Person實例化出來的對象都會繼承函數的原型

      缺點:當我們修改其中一個對象的屬性時,另一個對象下的也會被修改

      me.friends.push('jc'); console.log(you.friends);//["dw","xy","jc"]

      認同度最高的一種創建自定義對象的模式

      function Person(name,age){ // 定制當前對象自己的屬性 this.name = name; this.age = age; this.friends = ['dw','xy']; }; //定制公共的方法 Person.prototype = { constructor: Person, sayName: function(){ console.log(this.name); } } var wo = new Person('wo', 18); var you = new Person('you', 20);

      解決了原型模式狀態共享問題,將私有的屬性定義在函數內部,共有的方法通過原型去實現繼承引用

      原型鏈繼承的多種方式

      在原型對象的所有屬性和方法都能被實例所共享

      通過重寫原型對象的方式,將一個父對象的屬性和方法作為子對象的原型對象的屬性和方法

      function Animal(){ this.name = 'alex'; this.colors = ['red','green','blue']; } Animal.prototype.getName = function(){ return this.name; }//添加方法 function Dog(){}; Dog.prototype = new Animal();//將animal的原型對象繼承給dog Dog.prototype.constructor = Dog;//指向dog var d1 = new Dog(); var d2 = new Dog();

      存在問題:

      父類中的實例屬性一旦賦值給子類的原型屬性,此時這些屬性都屬于子類的共享屬性,修改一個,其他的都會被修改

      實例化子類型的時候,不能向父類型的構造函數傳參

      通過在子類構造函數內部調用父類構造函數,實現繼承,解決了原型鏈繼承存在的問題

      使用到了call關鍵字,改變當前函數的this指向當前的實例對象

      在new的過程中會將this指向當前的實例對象,因此在構造函數內部的this指向當前的實例對象

      function Animal(name){ this.name = name; this.colors = ['red','green','blue']; } Animal.prototype.getName = function(){ return this.name; } function Dog(name){ Animal.call(this,name); } var d1 = new Dog('阿黃'); var d2 = new Dog('小紅');

      **存在問題:**父類定義的==共享方法==不能被子類所繼承下來,只能繼承屬性

      原型鏈繼承和借助構造函數繼承結合,將2者的優點集合在一起

      function Animal(name) { this.name = name; this.colors = ['red', 'green', 'blue']; } Animal.prototype.getName = function() { return this.name; } function Dog(name) { Animal.call(this, name);//借助構造函數繼承 } Dog.prototype = new Animal();//原型鏈繼承 Dog.prototype.constructor = Dog; var d1 = new Dog('阿黃'); var d2 = new Dog('阿紅');

      利用原型鏈繼承的方式(重寫原型對象),將父類的共享方法繼承下來,同時在子類構造函數中調用父類構造函數,使得修改一方的值,不影響另一方的值

      **存在問題:**無論在什么情況下,都要調用父類構造函數2次

      利用Object.create方法將父類的原型對象傳入給子類的原型對象

      將組合模式繼承中的第11行代碼改為以下即可

      Dog.prototype = Object.create(Animal.prototype)

      利用Object.assign方法來拷貝原型對象,從而實現多重繼承(混入技術Mixin)

      Object.assign() 方法用于將所有可枚舉屬性的值從一個或多個源對象分配到目標對象。它將返回目標對象。

      Me.prototype = Object.create(Person.prototype); Object.assign(Me.prototype, Parent.prototype);

      通過這樣的方式,將父類構造函數的原型對象拷貝到本身的構造函數上,同時實現了將多個父函數的原型綁定到一個子元素上

      Object的靜態方法

      參數是一個對象,返回是一個數組,可枚舉的才返回,等同于for...in循環

      let a = { name:'ljc', age:19 } let arr = ['l','j','c']; console.log(Object.keys(arr));// ["0", "1", "2"] console.log(Object.keys(a));// ["name", "age"]

      注意:數組也是一個對象,可以作為參數傳進去,返回的是數組索引

      接收一個對象作為參數,返回了一個數組,包含了該對象自身的所有屬性名,包括不可枚舉的屬性

      let arr = ['l','j','c']; console.log(Object.getOwnPropertyNames(arr));// ["0", "1", "2", "length"]

      參數是一個對象,返回該對象的原型,也是獲取原型對象的標準方法

      function Fn() {} let f1 = new Fn(); console.log(Object.getPrototypeOf(f1) === Fn.prototype);//true

      等同于構造函數的prototype方法

      注意:空對象的原型是Object.prototype

      console.log(Object.getPrototypeOf({}) === Object.prototype);//true

      注意:函數的原型是Function.prototype

      console.log(Object.getPrototypeOf(foo) === Function.prototype)//true

      接收兩個參數,第一個參數是現有對象,第二個參數是原型對象,把第二個作為第一個的原型

      var a = {}; var b = { x: 1 }; Object.setPrototypeOf(a, b);//把b作為a的原型 console.log(a.x);// 1 console.log(Object.getPrototypeOf(a))// {x: 1}

      模擬new關鍵字

      function F(){ this.foo = 'foo'; } var f = Object.setPrototypeOf({},F.prototype);//創建空對象,繼承原型,返回對象 F.call(f);//改變this指向 console.log(f);// F {foo: "foo"}

      通過Object.setPrototypeof方法可以模擬實現new內部原理

      創建一個新對象,使用現有的對象(傳入的參數)來提供新創建的對象的__proto__,也就是將這個參數作為這個新對象的原型

      var A = { say :function() { console.log('ljc'); } } var B = Object.create(A) B.say();// ljc

      該方法還有第二個參數,有點復雜 MDN鏈接

      Object上的更多方法

      返回指定對象的原始值。

      // Function:返回函數本身 function foo(){} console.log( foo.valueOf() === foo ); // true // Array:返回數組對象本身 var array = ["ABC", true, 12, -5]; console.log(array.valueOf() === array); // true

      當遇到要預期的原始值的對象時,javaScript會自動調用它。

      我們可以根據這樣的特性來自定義這個方法,從而實現我們想要的操作

      var obj = new Object() obj.valueOf = function(){ return 2; } console.log(obj.valueOf() === obj); console.log(1 + obj);// 1[object Object] ------> 3

      原理:valueof是屬于Object原型對象的方法,Object.valueof的層級會優先于原型對象內的方法,所以會先調用我們自定義的valueof方法

      返回一個表示該對象的字符串。

      var o = new Object(); o.toString(); // returns [object Object]

      覆蓋默認的toString方法

      function Dog(name,breed,color,sex) { this.name = name; this.color = color; } var theDog = new Dog("Gabby", "chocolate"); Dog.prototype.toString = function dogToString() { var ret = "Dog " + this.name + " is " + this.color + " " ; return ret;// 'Dog GAbby' is chocolate }

      注意:在每個不同的數據類型中,定義了自己的toString方法

      返回一個該對象的字符串表示,字符串的形式與用戶地區有關

      Array:Array.prototype.toLocaleString()

      Number:Number.prototype.toLocaleString()

      Date:Date.prototype.toLocaleString()

      這幾個感覺有點說不清楚,這部分來自MDN文檔

      用于檢測一個對象是否存在于另一個對象的原型鏈上

      JavaScript面向對象編程詳解

      instanceof 運算符用于檢測構造函數的 prototype 屬性是否出現在某個實例對象的原型鏈上

      區別: inPrototypeOf是判斷這個對象是不是另一個對象的兒子,instanceof是判斷這個實例對象是不是由這個構造函數創建的

      方法會返回一個布爾值,指示對象自身屬性中是否具有指定的屬性

      o = new Object(); o.prop = 'exists'; o.hasOwnProperty('prop');

      注意:只有自身的屬性才為true,繼承來的屬性返回false,

      通過getOwnPropertyDescriptor屬性獲得描述屬性,只能獲取自身的屬性,不能獲取繼承的屬性

      writable:是否可寫

      enumerable:是否可枚舉

      configurable:是否可配置,例如刪除

      set:作為屬性的 setter 函數,如果沒有 setter 則為undefined。函數將僅接受參數賦值給該屬性的新值。

      get:作為該屬性的 getter 函數,如果沒有 getter 則為undefined。函數返回值將被用作屬性的值。

      var obj = { name:'ljc' } console.log(Object.getOwnPropertyDescriptor(obj,'name')); //{value: "ljc", writable: true, enumerable: true, configurable: true}

      返回一個布爾值,表示指定的屬性是否可枚舉

      var arr = [1,2,3]; console.log(arr.propertyIsEnumerable('length'));//false

      注意:只能判斷實例對象自身的屬性,不能判斷繼承來的屬性,否則為false

      直接在一個對象上定義一個新屬性,或者修改一個對象的現有屬性,并返回此對象。

      傳參方式

      Object.defineProperty(屬性所在的對象,屬性名,屬性描述對象)

      示例

      let obj = {}; Object.defineProperty(obj, "key", { enumerable: false, configurable: false, writable: false, value: "ljc" }); console.log(obj)// {key: "ljc"}

      方法直接在一個對象上定義新的屬性或修改現有屬性,并返回該對象。可以一次定義多個屬性

      示例

      var obj = Object.defineProperties({},{ p1:{ value:123, enumerable:true }, p2:{ value:"ljc", enumerable:false }, p3:{ get:function(){ return this.p1 + this.p2; }, enumerable:true, configurable:true } }) console.log(obj);// {p1: 666, p2: "ljc"} console.log(obj.p1);// 666

      其中的p2屬性顏色變淺,因為設置了不可枚舉

      注意:一旦定義了取值函數get,就不能同時定義value屬性,否則會報錯,也不能設置writable屬性

      注意:調用了該方法但有傳一個空對象,所有值都會被設為false

      var obj4 = Object.defineProperty({},'foo',{});

      在不可寫的情況下,更改屬性值,不會報錯,也不會更改

      幾個注意點

      如果enumrable設置false,通常以下三個操作不會取到該屬性

      for...in

      Object.keys()

      JSON.stringify()

      注意:雖然不能獲取自身的,但是可以獲取繼承來的屬性

      如果configurable為false,屬性描述對象 value,writable,enumrable,configurable都不能被修改

      writable,true改為false會允許的

      value屬性 只要writable和 configurable有一個為true,就允許被修改

      get方法,在獲取屬性時會自動調用這個方法

      set方法,在修改屬性時會被自動調用,需要一個參數

      let obj = { num: 2, get a() { return '我被調用了,我的值自增' + ++this.num },set a(value) { console.log('我被改了'); } } console.log(obj.a);//我被調用了,我的值自增3 obj.a = 2;//我被改了

      有關面向對象的知識就到這了!

      在ES6中新增了class類的關鍵字,以及一些相關屬性,優化了先前的面向對象代碼可讀性低的問題,新的class寫法讓對象原型的寫法更加清晰,更加的像面向對象編程的語法,因此class也只是一個語法糖,其所有內容都可以通過es5中對象原型方式實現

      javaScript 面向對象編程

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:Excel 如何讓多人同時編輯
      下一篇:怎么查找規定選項(根據指定的查詢條件)
      相關文章
      亚洲人成免费电影| 久久夜色精品国产亚洲AV动态图| 不卡一卡二卡三亚洲| 亚洲欧美日韩中文字幕在线一区| 水蜜桃亚洲一二三四在线| 亚洲乱码一区二区三区在线观看| 国产成人综合亚洲AV第一页| 亚洲精品网站在线观看不卡无广告| 国产亚洲精品成人久久网站| 在线91精品亚洲网站精品成人| 国产精品亚洲一区二区三区久久| 亚洲国产成人久久精品大牛影视 | 亚洲另类无码一区二区三区| 久久综合久久综合亚洲| 亚洲成_人网站图片| 中文字幕在线观看亚洲视频| 香蕉大伊亚洲人在线观看| 亚洲日韩中文字幕无码一区| 亚洲av永久无码| 国产亚洲精彩视频| 国产av无码专区亚洲国产精品| 久久精品国产精品亚洲人人| 亚洲线精品一区二区三区| 亚洲国产精品一区二区成人片国内| 亚洲av综合avav中文| 亚洲黄色高清视频| 亚洲宅男精品一区在线观看| 亚洲精品乱码久久久久久蜜桃图片| 久久亚洲精品11p| 亚洲成av人片在线观看天堂无码 | 亚洲成在人线中文字幕| 亚洲日本乱码卡2卡3卡新区| 亚洲色欲色欱wwW在线| 国产亚洲美女精品久久久久| 国产AⅤ无码专区亚洲AV| 久久精品夜色国产亚洲av| 亚洲综合久久久久久中文字幕| 国产午夜亚洲精品| 亚洲国产精品成人久久蜜臀| 在线亚洲人成电影网站色www| 亚洲国语精品自产拍在线观看|