面对对象回顾
通过new构造函数的方法
-
class的constructor ( es6 )
class Person{ constructor(name,age){ //构造器 this.name=name } } var person = new Person('小池')
- function的方式 ( es3 )
function Person(name){ this.name=name } var person = new Person('jack')
步骤
-
自动构建对象
-
手动设置属性
-
自动返回对象
通过工厂模式
function factory(name){ var obj=new Objtect() obj.name=name return obj }
步骤
-
手动构建对象
-
手动设置属性
-
手动返回对象
构造函数的缺陷
-
因为上面的两个sayhi方法不是同一个方法 , 所有每次new 都会重新在对应的堆上开辟内存
-
如果new操作很多 , 那sayhi方法占用的内存就越来越多 ( 效率就低了 )
-
按照我们的思考 , sayhi方法做的事情是一样的 , 那么我们其实只需要一个sayhi方法就够了
function Person(name) { this.name = name this.age = 18 this.sayhi = () => { console.log('hello') } } var person = new Person('jack') var person1 = new Person('tom') console.log(person == person1) //false 对应的存储地址不一样 console.log(preson.age == person1.age) //true console.log(person.sayhi == person1.sayhi) //false 函数也是引用类型,它比较的是存储地址
解决方案
-
找一个共享的空间 ( 对象 ) 只声明一次 , 将这个函数放进去 便可实现复用
-
它的这个共享空间 是属于 构造函数的
-
这个构造函数的空间就被称为 原型
原型
prototype
概述 : prototype是属于函数的一个空间 , 他是一个对象 ,因为构造函数也是函数所以它也具备 ,而这个prototype属性称为显示原型
函数的prototype
function fn(){ } console.log(fn.prototype)
prototype显示的是一个对象 , 里面具备对应的属性和方法 ( 主要是方法 )
构造函数的prototype ( 首字母大写 )
function Person() { } console.log(Person.prototype) // 获取当前的构造函数 console.log(Person.prototype.constructor) Person.prototype.sayHello = () => { console.log(this) } // 新建对象 var person = new Person() var person1 = new Person() console.log(person == person1) // prototype上存储的内容,可通过实例对象.属性名访问 console.log(person.sayHello == person1.sayHello)
-
从上可得构造函数的prototype是一个对象 , 里面有个属性 constructor 指向当前的构造函数 . 一般将对应的函数存储在prototype内
-
一般将函数 ( 方法 ) 存储在prototype上 ( 这个函数只会声明一次 ) . 将函数 ( 方法 ) 存储在原型 , 将属性放在构造函数里面
-
实例对象访问prototype上的内容可以通过实例对象.属性名访问
-
在prototype里面声明的函数的this指向当前调用的实例对象
__proto__
概述 : __proto__
称为隐式原型 , 它属于对象的一个空间 , 每个对象都存在这个空间 , 那么对应的实例对象也是一个对象 , 它也有这个空间 . 这个空间是指向对应的构造函数的prototype.
var obj = new Object() console.log(obj.__proto__) // 对象的__proto__指向对应的构造函数的prototype console.log(obj.__proto__ == Object.prototype) //true function Person() { } var person = new Person() console.log(person.__proto__ == Person.prototype) //true person.__proto__.sayhi = () => { console.log('hello world') } person.sayhi() //hello world
__proto__
的指向问题
__proto__
指向的是对应构造函数的原型 ( prototype )
构造函数也是一个对象 , 那么它的__proto__
也是指向父类的构造函数的原型
Object的__proto__
指向null , 返回的是undefined
//指向父类构造函数的原型 console.log(person.__proto__)//Person.prototype //指向父类构造函数的原型再向上 指向父类的父类的构造函数原型 ,是Object.prototype console.log(person.__proto__.__proto__)//Object.prototype //Object.prototype的__proto__指向null console.log(person.__proto__.__proto__.__proto__)//null
原型链
概述 : 对象在__proto__
上找属性的链式结构被称为原型链
从上指向问题来看 , 对象在原型上找属性 的过程为 :
-
先找自己的
__proto__
, ( 对应构造函数的prototype ) -
再找父类构造函数的原型 , 再找对应的父类的原型 , 直到找到objtect为止
-
object上还找不到返回null ( null的值是undefined )
Object.prototype.sex = '男' class Person { constructor() { this.name = 'jack' this.age = 24 } } Person.prototype.aloha = '111' class Son extends Person { constructor() { super() this.name = 'rose' } } // 对象赋值,有这个属性就重新赋值,没有就添加这个属性 Son.hi = 23 Son.prototype.sayHi = '侬好哇' // 实例化对象 var son = new Son() console.log(son.name) //rose console.log(son.age) //24 console.log(son.sayHi) //侬好哇 console.log(son.aloha) //111 console.log(son.sex) //男 console.log(son.hi) //undefined
注意事项
-
原型链不包含对象赋值
-
对象赋值的操作是 有这个属性就重新设置值 ,
-
没有 就添加这个属性进行赋值
-
对象赋值跟原型链没有关系
总结
-
构造函数的原型叫prototype ,
-
实例对象的原型是
__proto__
-
实例对象的
__proto__
指向构造函数的prototype -
原型链是通过对象的
__proto__
去找对应的属性 , 直到找到Objtect为止 -
原型上一般写函数 , 是可以保证函数只声明了一次 , 属性一般写在构造函数内
-
原型上的方法 / 属性 可以通过实例对象.属性名直接访问 ( 平常通过对象.的方法都是原型的方法 )
通过原型来实现数组的高阶函数
-
forEach
// 数组的构造函数内的原型添加一个myForEach方法 Array.prototype.myForEach = function (fn) { for (let i = 0; i < this.length; i++) { fn(this[i], i, this) } } var arr = [1, 2, 3, 4, 5] arr.myForEach((item, index, arr) => { console.log(item) })
- map
// 数组的构造函数内的原型添加一个myForEach方法 Array.prototype.myMap = function (fn) { let returnArr = [] for (let i = 0; i < this.length; i++) { returnArr.push(fn(this[i], i, this)) } return returnArr } var mapArr = arr.myMap((item, index, arr) => { return index }) console.log(mapArr)
- reduce
Array.prototype.myReduce = function (callback, init) { let prev = this[0] let currentIndex = 1 if (typeof init != 'undefined') { prev = init currentIndex = 0 } for (let i = 0; i < this.length; i++) { prev = callback(prev, this[i], i, this) } return prev } let reduceArr = arr.myReduce((prev, current) => { return prev + current }) console.log(reduceArr)
面对对象的三大特性
-
封装 ( 把相同的属性和方法提取出来 )
-
继承
-
多态
继承
概述 : 子类继承父类的属性和方法 ( 非私有的属性和方法 , 非静态的方法 )
继承的实现
-
使用extends 关键词实现继承 ( es6类的继承 ) 常用 , 有兼容问题
// es6类的继承 使用extends class Person { constructor() { this.name = 'jack' } } class Son extends Person { constructor() { super() this.age = 22 } } var son = new Son() console.log(son) //name:jack,age:22
-
原型链继承 ( 会覆盖之前原型上的所有方法 ( 放继承后面就不会覆盖 ) , 不会显示继承来的属性 , 只能再原型上显示 )
核心 : 在子类的原型上创建父类的原型对象
//原型链继承 function Person() { this.name = 'jack' } function Son(age) { this.age = age } Son.prototype.say = () => { console.log('侬好侬好') } // 原型链继承 Son.prototype = new Person() var son = new Son(22) console.log(son) son.say() //会报错 , 本身设置的原型方法会被继承来的全部覆盖
- 对象冒充 ( 会显示继承来的属性 , 不能继承原型上的方法 )
核心 : 通过改变父类构造函数的this 实现
//对象冒充 通过改变父类构造函数的this实现 //不能继承原型上的方法 function Person() { this.name = 'rose' } Person.prototype.hi = () => { console.log('aloha'); } function Son(age) { // 对象冒充 : 通过改变父类构造函数的this来实现 Person.call(this) this.age = age } var son = new Son(24) console.log(son) son.hi() //会报错 , 不能继承原型上的方法
- 组合继承 ( 使用原型链继承和对象冒充结合 , 会显示多余的内容 , 多余的内容是父类的属性 )
// 组合继承 (对象冒充+原型链继承) // 可以继承本身的原型方法,但是会有多的内容 function Person() { this.name = 'rose' } // Person上的原型say方法 Person.prototype.say = () => { console.log('aloha') } function Son() { // 对象冒充,改变父类构造函数的this实现 Person.call(this) this.age = 25 } // 原型链继承 Son.prototype = new Person() var son = new Son() console.log(son) son.say() //aloha
- 组合寄生继承 ( 对象冒充 + 原型链 ( 通过创建一个原型对象放在原型链上 ) 可以拿到本身和继承的属性和方法 , 也不会显示多余的内容 )
//组合寄生继承 (冒充继承+原型链继承(通过创建一个原型对象)) //可以拿到本身和继承的属性和方法,也不会显示多余的内容 function Person() { this.name = 'Alex' } Person.prototype.sayHi = () => { console.log('long time no see') } function Son() { //冒充继承 Person.apply(this) this.age = 25 } // 创建一个原型对象 Son.prototype = Object.create(Person.prototype) var son = new Son() console.log(son) son.sayHi()
轮播图面对对象
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> * { margin: 0; padding: 0; } .showBox, li { width: 400px; height: 250px; } li>img { width: 100%; } .showBox { position: relative; margin: 200px auto; /* 溢出隐藏 */ overflow: hidden; } ul { width: 800%; position: relative; } ul>li { list-style: none; float: left; } .cirList { position: absolute; right: 20px; bottom: 10px; width: 150px; } .cirList>li { width: 10px; height: 10px; background-color: #fff; border-radius: 50%; margin: 0 5px; } .cirList .selected { background-color: red; } .arrow { display: none; } .arrow>a { display: block; width: 50px; height: 50px; position: absolute; top: 50%; margin-top: -25px; } .arrow>.prev { background: url(./images/prev.png) no-repeat center; background-size: contain; left: 0; } .arrow>.next { background: url(./images/next.png) no-repeat center; background-size: contain; right: 0; } </style> </head> <body> <div class="showBox"> <ul class="nav"> <li><img src="./images/slidepic1.jpg" alt=""></li> <li><img src="./images/slidepic2.jpg" alt=""></li> <li><img src="./images/slidepic3.jpg" alt=""></li> <li><img src="./images/slidepic4.jpg" alt=""></li> <li><img src="./images/slidepic5.jpg" alt=""></li> <li><img src="./images/slidepic6.jpg" alt=""></li> <li><img src="./images/slidepic7.jpg" alt=""></li> </ul> <!-- 焦点 --> <ul class="cirList"> </ul> <div class="arrow"> <a href="" class="prev"></a> <a href="" class="next"></a> </div> </div> <script src='https://cdn.bootcdn.net/ajax/libs/move.js/0.5.0/move.js'></script> </body> </html> <script> class Carousel { constructor(box) { this.box = box // 根据大盒子获取存储图片的盒子(nav) this.nav = box.querySelector('.nav') // 根据大盒子获取存储焦点的盒子 this.cirList = box.querySelector('.cirList') // 控制的下标默认设为0 this.index = 0 this.init() //添加焦点 this.handlerClick() } init() { // 根据图片个数来添加焦点(先克隆第一张图片到最后),因为克隆的图片不需要添加,所有放在克隆前 // 把this.nav.children当做数组来调用forEach方法 Array.prototype.forEach.call(this.nav.children, (item, index) => { let htmlCode = '<li></li>' // 给第一个li添加焦点和class样式 if (index == 0) { htmlCode = '<li class="selected"></li>' } // 其余的添加焦点 this.cirList.innerHTML += htmlCode }) // 克隆第一张图片放到最后一张后面 var cloneNode = this.nav.children[0].cloneNode(true) this.nav.appendChild(cloneNode) } // 移动的方法 区分正反方向,默认是正 move(direction = true) { // 区间判断 // 如果是第一张图片并且是反方向,就把它切换到图片最后一张,再设置位置 if (this.index < 1 && !direction) { this.index = this.nav.children.length - 1 this.nav.style.transform = `translate3d(${this.index * this.box.offsetWidth * -1}px,0px,0px)` } // 如果是最后一张(因为克隆了一张所以需要-2)并且是正方向,就把它切换到第一张,在设置位置 if (this.index > this.nav.children.length - 2 && direction) { this.index = 0 this.nav.style.transform = 'translate3d(0px,0px,0px)' } // 正方向下标就加加 if (direction) { this.index++ } else { // 反方向就减减 this.index-- } // 调用第三方插件 x是需要设置宽度(左右切换) var x = this.index * this.box.offsetWidth * -1 move('.nav').to(x, 0).duration('1s').end() this.foucsEvent(this.index) } // 自动轮播,用定时器实现 autoMove() { let self = this this.timer = setInterval(() => { // 调用第三方move方法 self.move() }, 2000) } // 焦点切换 foucsEvent(index) { // 遍历并设置样式 Array.prototype.forEach.call(this.cirList.children, (item, i) => { item.className = (i == index ? 'selected' : '') }) } // 焦点点击 handlerClick() { let self = this // 遍历并给每个成员设置点击事件 Array.prototype.forEach.call(this.cirList.children, (item, i) => { let index = i item.onclick = () => { // 点击的下标应该是当前下标-1,因为点击的下标是0-6,图片切换的下标是1-7 self.index = index - 1 self.move() } }) } } // 获取大盒子 var box = document.querySelector('.showBox') // 调用轮播图方法 var carousel = new Carousel(box) // 调用自动轮播 carousel.autoMove() </script>
es6的模块化
模块的思想 , 就是将对应的功能代码封装为一个模块 ( js代码 , css代码 , html代码 )
想要使用别人的就导入 , 想要给别人用就导出 , 复用性强
模块化的常用模式
amd ( 在加载之前就导入 )
cmd ( 在用的时候导入 )
comment . js ( 基于amd和cmd之上 )
es6模块化的关键词 ( 要想导入先导出 )
import 导入
inport 名字(obj/{obj,str,say}/{name}) from '地址'
export 导出
第一种 : export default ( 只能声明一次 )
const obj = { name: "jack", age: 24, sex: '男' } const str = 'hello world' const say = function () { console.log('aloha') } // 默认只能导出一个,导出时可以使用对应的名字来接(这里写的是obj) export default { obj, str, say }
接收
<script type="module"> import obj from './test.js' console.log(obj) console.log(obj.str) console.log(obj.say) console.log(obj.obj) </script>
第二种 : export 加在声明的变量前
export const obj = { name: "jack", age: 24, sex: '男' } export const str = 'hello world' export const say = function () { console.log('aloha') }
接收
<script type="module"> import {obj,str,say} from './test.js' console.log(obj) console.log(str) console.log(say) </script>
第三种 : 直接写对象
const obj = { name: "jack", age: 24, sex: '男' } export {obj} const str = 'hello world' export {str} const say = function () { console.log('aloha') } export {say}
接收
<script type="module"> import { str,obj,say } from './test.js' console.log(obj) console.log(str) console.log(say) </script>标签:console,log,继承,Person,原型,prototype,day19,构造函数 From: https://www.cnblogs.com/itlulu/p/16826560.html