>### 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>
```