>### A.今天學什么?
#### 1.js的面向對象
- ##### 1.1 js如何實現面向對象
- js在ES6之前并沒有類的概念
- js如何實現面向對象 --> 構造函數模擬類
- ##### 1.2 js使用構造函數模擬類時如何定義方法
- js的繼承是基于原型的繼承。原型 --> prototype
- 將方法寫在原型上,所有實例化的對象,就可以使用該方法
- ##### 1.3 js如何創建實例對象
- 使用new關鍵字實例化對象,類似于java中創建實例對象
```
<body>
<script>
// js如何實現面向對象 --> 構造函數模擬類,ES6之前沒有類的概念
function Person(age,name) {
this.age = age;
this.name = name;
}
// js的繼承是基于原型的繼承。原型 --> prototype
// 將方法寫在原型上,所有實例化的對象,就可以使用該方法
Person.prototype.say = function () {
console.log(this.name+"在說話");
};
// 使用new關鍵字實例化對象
var yiran = new Person(21,"yiran");
console.log(yiran.name+":"+yiran.age); // yiran:21
yiran.say(); // yiran在說話
</script>
</body>
```
- ##### 1.4 js的繼承是基于原型的繼承,嘗試給Array的原型增加一個eat()方法
- 并定義一個數組,使用該數組調用eat()方法
```
// body
<body>
<script>
// 嘗試給Array數組的原型上定義一個eat方法
Array.prototype.eat = function () {
console.log("數組也會吃?");
};
// 定義一個數組
var a = [1,2,3];
// 成功了
a.eat(); // 數組也會吃?
</script>
</body>
```
- ##### 1.5 實例對象的__proto__屬性
- 每個對象都有一個__proto__屬性,指向它的原型對象,共享原型上的屬性或方法
- 嘗試看一下 Person原型的__proto__屬性 --> Objcet的原型
- Person原型的原型即為Object的原型
```
// body
<body>
<script>
// 每個對象都有一個__proto__屬性,指向它的原型對象,共享原型上的屬性或方法
console.log(yiran.__proto__);
console.log(yiran.__proto__ === Person.prototype); // true
// 嘗試看一下 Person.prototype.__proto__ --> Object的原型
// 原型鏈,就相當于java中的Object是List的父類,List是ArrayList的父類一樣
console.log(Person.prototype.__proto__); // obj
console.log(Person.prototype.__proto__ === Object.prototype); // true
</script>
</body>
```
- ##### 1.6 改變this指向 --> call()和apply()
- 一般方法調用時,this都是指向window
- call與apply的區別 --> 區別在于對有參函數的傳參方式不同
- func.call() --> 第一位參數改變this指向,其余正常傳參
- func.apply() --> 第一位參數改變this指向,第二位參數為方法參數列表,為一個數組
- 個人認為 apply()比較方便理解
```
// body
<body>
<script>
var name = "yiran";
function show() {
console.log(this.name);
}
show(); // yiran --> 為什么?因為name也屬于window,window里定義的name即為yiran
var yiran = {
name:"yiran",
age:21
};
// call,apply改變函數內部this關鍵字的指向
show.call(yiran); // yiran --> 改變函數內部this的指向為對象yiran,yiran的name屬性值為"yiran"
show.apply(yiran); // yiran --> 同理
// call與apply的區別 --> 區別在于對有參函數的傳參方式不同
function show2(a,b) {
console.log(a+b);
console.log(this.name);
}
var chunjue = {
name:"chunjue"
};
// call() --> 第一位參數改變this指向,其余正常傳參
// apply() --> 第一位參數改變this指向,第二位參數為方法參數列表,為一個數組
// 個人認為 apply()比較方便理解
show2.call(chunjue,1,2);
show2.apply(chunjue,[1,2]);
</script>
</body>
```
- ##### 1.7 另一種改變this指向的方法 --> bind()
- bind()與其他兩種方法不同之處在于,bind()是在方法定義時的尾部添加,而另外兩種方法是調用方法時使用。
- 傳參與call()相同,除了第一位更改this指向,其余順序傳參
```
// body
<body>
<button id="btn">點擊</button>
<p id="p">hello world</p>
<script>
// bind() --> 同樣改變this的指向,在方法的末尾使用,只是改變上下文的函數副本,不會執行函數
// 傳參與call()相同,除了第一位更改this指向,其余順序傳參
var btn = document.getElementById("btn");
var p = document.getElementById("p");
btn.onclick = function (a,b) {
console.log(a+b);
console.log(this.id);
}.bind(p,1,2);
</script>
</body>
```
- ##### 1.8 定時器this問題
- 函數作為普通函數調用時,this指向window
- 使用bind()方法更改this指向
```
// body
<body>
<button id="btn">點擊</button>
<script>
// 函數作為普通函數調用時,this指向window
var id = "world";
var btn = document.getElementById("btn");
btn.onclick = function () {
// 外部的this則是btn,因為是btn點擊后觸發的
console.log("外部this:"+this.id);
// 實際上這里是window.setTimeout(),所以this指向window,取出window的id屬性
// 但是如果在setTimeout()方法后綁定bind(),則可以改變指向
setTimeout(function () {
console.log("內部this:"+this.id); // world
}.bind(this),1000)
}
</script>
</body>
```
- ##### 1.9 ES6箭頭函數解決this指向問題
- ES6箭頭函數,方法中的所有this都指向同一個,在這里也就是btn
```
// body
<body>
<button id="btn">點擊</button>
<script>
var btn = document.getElementById("btn");
btn.onclick = function () {
// 外部的this則是btn,因為是btn點擊后觸發的
console.log("外部this:"+this.id);
// ES6箭頭函數,方法中的所有this都指向同一個,在這里也就是btn
setTimeout(() => {
console.log("內部this:"+this.id); // world
},1000)
}
</script>
</body>
```
- ##### 1.10 另一種解決this指向的方法,定義變量存儲
- 使用變量接收外部this,在內部使用
```
// body
<body>
<button id="btn">點擊</button>
<script>
// 另一種在里函數中獲得外部this的方法
// 使用變量接收外部this,在內部使用
var btn = document.getElementById("btn");
btn.onclick = function () {
var self = this;
setTimeout(function () {
console.log(self.id);
},1000)
}
</script>
</body>
```
#### 2.js繼承
- ##### 2.1 ES6之前js實現模擬類
- 定義方法模擬類 --> function ClassName(參數){this.參數=參數}
- ##### 2.2 屬性的繼承
- 通過call或apply改變函數(子類)內部this關鍵字的指向,在函數(子類)內部調用父類函數
- 方法中參數定義的屬性,為自有屬性,并不是原型屬性,這里要注意
- ##### 2.3 方法的繼承
- 將其原型指向父類的實例即可,就可以得到父類實例中的方法,因為 Person的實例中有一個__proto__屬性指向Person的原型prototype,其中存儲了方法,現在讓Teacher的原型指向Person實例指向的地方,相當于讓Teacher的__proto__也指向了Person的原型prototype,這樣,就獲得了Person的方法
- 即 --> Teacher.prototype = new Person();
```
// body
<body>
<script>
function Person(name,age) {
// 這里的屬性是自有的,并不是原型的屬性
this.name = name;
this.age = age;
}
// 這種屬性才叫原型的屬性,只不過這個屬性是一個方法
Person.prototype.say = function () {
console.log(this.name+"開啟嘴炮模式");
};
function Teacher(name,age) {
// js實現繼承
console.log(this); // 這里的this是指向Teacher的
// 屬性的繼承:
// 通過call或apply改變函數內部this關鍵字的指向
Person.call(this,name,age);
}
// 方法的繼承:
// 將其原型指向父類的實例即可,就可以得到父類實例中的方法
// 因為 Person的實例中有一個__proto__屬性指向Person的原型prototype
// 其中存儲了方法,現在讓Teacher的原型指向Person實例指向的地方,相當于讓Teacher的__proto__也指向了
// Person的原型prototype,這樣,就獲得了Person的方法
Teacher.prototype = new Person();
var chunjue = new Teacher("chunjue",25);
console.log(chunjue.name);
</script>
</body>
```
- ##### 2.4 ES6實現繼承
- js的ES6中,可以定義類,定義語法與java類似
- js的ES6中,類里的構造函數無法重載,一個類只能有一個構造函數
- 類中無法像java一樣定義屬性,而是在構造方法中定義
```
// body
<body>
<script>
// js的ES6中,可以定義類,定義語法與java類似
// js的ES6中,類里的構造函數無法重載,一個類只能有一個構造函數
// 類中無法像java一樣定義屬性,而是在構造方法中定義
class Person{
constructor(name,age){
this.name = name;
this.age = age;
}
say(){
console.log(this.name+"又在嗶嗶了");
}
}
let chunjue = new Person("chunjue",25);
chunjue.say();
// 實現繼承,與java基本相同,super()方法同樣必須在構造函數的第一行
class Teacher extends Person{
constructor(name,age,sex){
super(name,age);
this.sex = sex;
}
eat(){
console.log(this.name+"老師又在吃飯了,性別為"+this.sex);
}
}
let zhuyue = new Teacher("zhuyue",22,"女");
zhuyue.say();
zhuyue.eat();
</script>
</body>
```
- ##### 2.5 ES6對象方法簡寫
```
// body
<body>
<script>
var obj = {
// 對象方法的正常寫法
go:function () {
console.log("go方法");
},
// ES6中,對象方法的簡寫
eat(){
console.log("eat方法");
}
};
obj.eat(); // 可以調用
</script>
</body>
```
#### 3.vue介紹
- ##### 3.1 vue可以通過vue實例來渲染某個標簽的內容
- 掛載點 --> 即為 vue.el屬性 選中的標簽
- 模板 --> 掛載點里的內容為模板
- 實例 --> new Vue({})
- 實例只會渲染掛載點
- ##### 3.2 v-text和v-html的區別
- v-text 會把數據所有的內容都當成文本去渲染
- v-html 把數據當成html標簽來渲染
- 這兩者作為標簽的屬性寫在標簽中,如\<div v-text="msg">\</div>
```
// body
<body>
<p id="test">
<!-- 模板 -->
{{msg}}
</p>
<div id="root">
</div>
<div id="root2">
<!-- v-text和v-html的區別
v-text 會把數據所有的內容都當成文本去渲染
v-html 把數據當成html標簽來渲染
-->
<div v-text="msg"></div>
<!--<div v-html="msg"></div>-->
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
<script>
// 1.new Vue實例 --> 實例只會渲染掛載點之中的內容
new Vue({
// 選中掛載點
el:"#test",
data:{
msg:"hello world"
}
});
// 2.將模板寫在實例中
new Vue({
el:"#root",
template:"<div>{{msg}}</div>",
data:{
msg:"hello chunjue"
}
});
// 3.v-text和v-html
new Vue({
el:"#root2",
data:{
msg:"<h1>hello chunjue</h1>"
}
})
</script>
</body>
```
- ##### 3.3 vue事件
- 事件作為屬性寫在標簽內部
- 事件綁定 --> @事件="事件名"
- 屬性綁定 --> :屬性="屬性值" 屬性值中可以使用字符串加減
```
// body
<body>
<!-- vue中的事件,事件名自定義 -->
<!-- 事件綁定 -- @事件="事件名" -->
<!-- 屬性綁定 -- :屬性="屬性值" 屬性值中可以使用字符串加減 -->
<!-- 這里綁定title屬性后,鼠標懸停會有提示信息,然后可以用屬性綁定來定義提示信息 -->
<div id="root" @click="handleClick" :title="'good! '+title">
{{msg}}
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
<script>
new Vue({
el:"#root",
data:{
msg:"hello chunjue",
title:"I'm yiran"
},
methods:{
handleClick:function () {
this.msg = "change"
}
}
});
</script>
</body>
```
- ##### 3.4 雙向數據綁定
- 雙向數據綁定 v-model 設置了該屬性的標簽,與內容為{{msg}}的標簽同步更新
- v-model這個模板指令表示雙向數據綁定
```
// body
<body>
<div id="root">
<!-- 雙向數據綁定 v-model 設置了該屬性的標簽,與內容為{{msg}}的標簽同步更新 -->
<input type="text" v-model="msg">
<p>{{msg}}</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
<script>
new Vue({
el:"#root",
data:{
msg:"hello chunjue"
}
});
</script>
</body>
```
- ##### 3.5 雙向數據綁定應用:computed計算屬性
- computed計算屬性 --> 可以計算擁有 v-model屬性的標簽的內容
```
// body
<body>
<div id="root">
<input type="text" v-model="firstName">
<input type="text" v-model="lastName">
<div>{{sum}}</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
<script>
new Vue({
el:"#root",
data:{
firstName:"1",
lastName:"2"
},
// computed計算屬性
computed:{
sum:function () {
// 其中也可以做if判斷,來判斷是否為數字,然后進行數字加減
return this.firstName + this.lastName
}
}
});
</script>
</body>
```
- ##### 3.6 偵聽器watch
- 偵聽某個數據的變化,一旦它變化就可以做業務邏輯
- 和雙向數據綁定一起使用
```
// body
<body>
<!-- 對姓名作任意變更,count加一 -->
<div id="root">
<input type="text" v-model="firstName">
<input type="text" v-model="lastName">
<div>{{fullName}}</div>
<div>count:+{{count}}</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
<script>
new Vue({
el:"#root",
// 這里給一個默認值,但是不能寫fullName的默認值,否則無法改變
data:{
firstName:'',
lastName:'',
count:0
},
computed:{
fullName:function(){
return this.firstName+" "+this.lastName;
}
},
watch:{
firstName:function(){
this.count++;
},
lastName:function(){
this.count++;
}
}
})
</script>
</body>
```
- ##### 3.7 v-if
- 控制DOM的存在與否,如果data中返回為false,則DOM移除,返回為true,則顯示
```
// body
<body>
<div id="root">
<!-- //控制DOM的存在與否,如果data中返回為false,則DOM刪除 -->
<div v-if="show">hello world</div>
<button @click="handleClick">toggle</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
<script>
new Vue({
el:"#root",
data : {
show:false
},
methods : {
handleClick : function(){
// 讓this.show 不等于 this.show 即
// 點擊一下,false變true,true變false
this.show =! this.show;
}
}
})
</script>
</body>
```