ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
>### 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> ```