JavaScript封裝 繼承 多態(tài)
1.封裝(和java,c++一樣)
1.1 首先了解一下什么是對象的私有變量和函數(shù)
- 默認(rèn)情況下對象中的屬性和方法都是公有的, 只要拿到對象就能操作對象的屬性和方法,外界不能直接訪問的變量和函數(shù)就是私有變量和是有函數(shù),構(gòu)造函數(shù)的本質(zhì)也是一個函數(shù), 所以也會開啟一個新的作用域, 所以在構(gòu)造函數(shù)中定義的變量和函數(shù)就是私有函數(shù)
1.2 什么是封裝
- 封裝性就是隱藏實現(xiàn)細(xì)節(jié)公浪,僅對外公開接口
1.3 為什么要封裝
- 不封裝的缺點(diǎn):當(dāng)一個類把自己的成員變量暴露給外部的時候,那么該類就失去對屬性的管理權(quán)惋嚎,別人可以任意的修改你的屬性
- 封裝就是將數(shù)據(jù)隱藏起來,只能用此類的方法才可以讀取或者設(shè)置數(shù)據(jù),不可被外部任意修改. 封裝是面向?qū)ο笤O(shè)計本質(zhì)(將變化隔離)守呜。這樣降低了數(shù)據(jù)被誤用的可能 (提高安全性和靈活性)
1.4 代碼說明
function Person() { this.name = "lnj"; // this.age = 34; let age = 34; this.setAge = function (myAge) { //封裝 if(myAge >= 0){ age = myAge; } } this.getAge = function () { //封裝 return age; } this.say = function () { console.log("hello world"); } } let obj = new Person(); // 結(jié)論: 默認(rèn)情況下對象的屬性和方法都是公開的, 只要拿到對象就可以操作對象的屬性和方法 // console.log(obj.name); // obj.age = -3; // console.log(obj.age); // obj.say(); // console.log(age); //封裝后就不可任意修改 obj.setAge(-3); console.log(obj.getAge());
1.5 私有屬性注意點(diǎn)
針對以上代碼,我們看如下操作的區(qū)別
let obj = new Person(); // 1.操作的是私有屬性(局部變量) obj.setAge(-3); console.log(obj.getAge()); /* // 注意點(diǎn): // 在給一個對象不存在的屬性設(shè)置值的時候, 不會去原型對象中查找, 如果當(dāng)前對象沒有就會給當(dāng)前對象新增一個不存在的屬性 // 由于私有屬性的本質(zhì)就是一個局部變量, 并不是真正的屬性, 所以如果通過 對象.xxx的方式是找不到私有屬性的, 所以會給當(dāng)前對象新增一個不存在的屬性 */ // 2.操作的是公有屬性 obj.age = -3; console.log(obj.age); //因此會返回-3 ,而不是34
1.6 JavaScript 屬性方法分類
-
靜態(tài)屬性/靜態(tài)方法
通過構(gòu)造函數(shù)訪問的屬性, 我們就稱之為靜態(tài)屬性
通過構(gòu)造函數(shù)調(diào)用的方法, 我們就稱之為靜態(tài)方法
function Person() { this.name = "lnj"; this.say = function () { console.log("hello world"); } } // 構(gòu)造函數(shù)也是一個"對象", 所以我們也可以給構(gòu)造函數(shù)動態(tài)添加屬性和方法 Person.num = 666; Person.run = function () { console.log("run"); } console.log(Person.num); Person.run();
-
實例屬性/實例方法
- 通過實例對象訪問的屬性, 我們就稱之為實例屬性
- 通過實例對象調(diào)用的方法, 我們就稱之為實例方法
2.繼承
繼承方式一 : 借用原型鏈實現(xiàn)繼承
直接將子類的原型對象修改為父類對象, 這樣就能使用原型鏈上的屬性和方法
function Person() { this.name = null; this.age = 0; this.say = function () { console.log(this.name, this.age); } } // 由于是直接將子類原型對象修改為了父類對象 // 所以繼承的屬性值很難自定義,當(dāng)父類的值需要傳進(jìn)去時,子類對象不能滿足這個功能 function Student() { this.score = 0; this.study = function () { console.log("day day up"); } } Student.prototype = new Person(); Student.prototype.constructor = Student; var stu1 = new Student(99); console.log(stu.name, stu.age, stu.gender, stu.score);
繼承方式二 : 借用構(gòu)造函數(shù)實現(xiàn)繼承
在子類中調(diào)用父類構(gòu)造函數(shù), 并且將父類構(gòu)造函數(shù)的this修改為子類對象
// 父類 function Person(name, age, gender) { this.name = name; this.age = age; this.gender = gender; } // 借用構(gòu)造函數(shù)只是調(diào)用了父類的構(gòu)造函數(shù), 借用了構(gòu)造函數(shù)中的代碼 // 相當(dāng)于動態(tài)的給子類添加了許多屬性, 但是并沒有修改子類的原型 // 所以子類無法繼承父類定義在prototype的方法和屬性 Person.prototype.say = function () { console.log(this.name, this.age, this.gender); } // 子類 function Student(score, name, age, gender) { Person.call(this, name, age, gender); this.score = score; } var stu1 = new Student(99, "lnj", 33, "male"); var stu2 = new Student(66, "zq", 18, "female"); console.log(stu1.name, stu1.age, stu1.gender, stu1.score); console.log(stu2.name, stu2.age, stu2.gender, stu2.score); stu1.say(); // 報錯 stu2.say(); // 報錯
繼承方式三 : 借用構(gòu)造函數(shù)+借用原型鏈組合繼承
通過借用構(gòu)造函數(shù)實現(xiàn)屬性繼承
通過借用原型鏈實現(xiàn)方法繼承
// 父類 function Person(name, age, gender) { this.name = name; this.age = age; this.gender = gender; } Person.prototype.say = function () { console.log(this.name, this.age, this.gender); } // 子類 function Student(score, name, age, gender) { Person.call(this, name, age, gender); this.score = score; } Student.prototype = Person.prototype; Student.prototype.constructor = Student; // 由于子類的原型指向了父類的原型, 所以操作的都是同一個原型對象 // 給子類的原型新增方法或者屬性, 父類也會受到影響 Student.prototype.study = function () { console.log("好好學(xué)習(xí)天天向上"); }; Student.prototype.type = "學(xué)生"; var stu = new Student(99, "lnj", 33, "male"); stu.say(); stu.study(); console.log(stu.type); var p = new Person("zq", 18, "female"); p.say(); p.study(); console.log(p.type);
繼承方式四 : 專業(yè)寫法
// 父類 function Person(name, age, gender) { this.name = name; this.age = age; this.gender = gender; } Person.prototype.say = function () { console.log(this.name, this.age, this.gender); } // 子類 function Student(score, name, age, gender) { Person.call(this, name, age, gender); this.score = score; } Student.prototype = new Person(); //區(qū)別就在這 Student.prototype.constructor = Student; // 由于子類的原型指向一個全新的對象 // 所以給子類的原型新增方法或者屬性, 父類不會受到影響 Student.prototype.study = function () { console.log("好好學(xué)習(xí)天天向上"); }; Student.prototype.type = "學(xué)生"; var stu = new Student(99, "lnj", 33, "male"); stu.say(); stu.study(); console.log(stu.type); var p = new Person("zq", 18, "female"); p.say(); p.study(); // 報錯 console.log(p.type); // 報錯
3.多態(tài)
- 3.1 什么是強(qiáng)類型語言:
一般編譯型語言都是強(qiáng)類型語言,
強(qiáng)類型語言蛋欣,要求變量的使用要嚴(yán)格符合定義
例如定義 int num; 那么num中將來就只能夠存儲整型數(shù)據(jù) - 3.2 什么是弱類型語言:
一般解釋型語言都是弱類型語言,
弱類型語言, 不會要求變量的使用要嚴(yán)格符合定義
例如定義 let num; num中既可以存儲整型, 也可以存儲布爾類型等 - 3.3 由于js語言是弱類型的語言, 所以我們==不用關(guān)注多態(tài)==
- 3.4 多態(tài)在編程語言中的體現(xiàn)
父類型變量保存子類型對象, 父類型變量當(dāng)前保存的對象不同, 產(chǎn)生的結(jié)果也不同