面向对象回顾
通过new构造函数的方式
class的construtor ( es6 )
class Person{
construtor(name){ //构造器
this.name = name
}
}
var person = new Person('tom')
function的方式 ( es3 )
function Person(name){
this.name = name
}
var person = new Person('jack')
步骤
-
自动构建对象
-
手动设置属性
-
自动返回对象
通过工厂模式
function factory(name){
var obj = new Object()
obj.name = name
return obj
}
步骤
-
手动构建对象
-
手动设置属性
-
手动返回对象
构造函数的缺陷
function Person (name){
this.name = name
this.age = 18
this.sayhello = ()=>{
console.log('hello');
}
}
var person = new Person('jack')
var person1 = new Person('tom')
console.log(person == person1);//false 对应的存储地址不一样
console.log(person.age == person1.age);//true 常量的值都为18
console.log(person.sayhello == person1.sayhello);//false 函数也是引用数据比较的是地址
原型
prototype
概述:prototype是属于函数的一个空间,它是一个对象,因为构造函数也是函数所以它也具备,而这个prototype属性我们称为显式原型。
函数的prototype
function fn(){
}
console.log(fn.prototype)
对应的这个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);//false
//person.sayHello ===> Person.prototype.sayHello
//对于prototype上存储的内容 通过实例对象.属性名访问
console.log(person.sayHello == person1.sayHello);//true
//对于prototype上存储的内容 通过实例对象.属性名访问
console.log(person.constructor);
-
从上可得构造函数的prototype是一个对象,第二个里面有个属性constructor指向当前的构造函数
-
实例对象访问对于的prototype上的内容可以通过实例对象.属性名访问
-
一般将对应的函数存储在对应prototype上(这个函数只会声明一次)。将函数存储在原型,将属性放在构造函数里面
-
在prototype里面声明的函数的this指向当前的调用的实例对象
__proto__
概述:
__proto__
称为隐式原型,它是属于对象的一个空间,每个对象都存在这个空间,那么对应的实例对象也是一个对象,所以它也有这个空间。这个空间指向对应的构造函数的prototype。
var obj = new Object()
//每个对象都存在的一个空间 它指向对应的构造函数的prototype
console.log(obj.__proto__);
//对象的__proto__指向对应的构造函数的prototype
console.log(obj.__proto__ == Object.prototype);
function Person(){
}
var person = new Person()
console.log(person.__proto__ == Person.prototype);
//Person.prototype.sayHello = ()=>{
//}
person.__proto__.sayHello = ()=>{
console.log('hello');
}
person.sayHello()
__proto__
的指向问题
__proto__
指向对应的构造函数的prototype
构造函数也是一个对象 那么它的__proto__
指向谁 指向对应的父类的构造函数的prototype
Object的__proto__
指向null
//指向Person.prototype
console.log(person.__proto__);
//指向构造函数的原型 构造函数的原型是啥 是一个对象 Object.prototype
console.log(person.__proto__.__proto__);
//指向构造函数的原型 构造函数的原型是啥 是一个对象 Object.prototype 的__proto__是null
console.log(person.__proto__.__proto__.__proto__);
原型链
概述:
对象在__proto__
原型上找属性的链式结构被称为原型链。
从上面的指向问题来看 对象在原型上找属性的过程为
-
先找自己的
__proto__
(对应的构造函数的prototype) -
再找对应的自身构造函数的原型的
__proto__
找到父类构造函数的原型,再找对应的父类的原型的__proto__
,直到找到object为止 -
Object的原型的
__proto__
(null)上还找不到返回undefined
Object.prototype.htllo = '你好'
class Person{
constructor(){
this.name = '张三'
}
}
Person.prototype.sex = '女'
Person.prototype.username = 'rose'
//对象赋值 有就重新赋值 没有就添加属性赋值
Person.hi = '你好吗'
var person = new Person()
person.age = '109'
class Son extends Person{
constructor(){
super()
this.age = '李四'
}
}
Son.prototype.sex = '男'
//实例化对象
var son = new Son()
console.log(son.age);//李四
console.log(son.name);//张三
console.log(son.username);//rose
console.log(son.sex);//男
console.log(son.hello);//你好
console.log(son.hi);//undefined
注意事项
-
原型链不包含对象赋值
-
对象赋值的操作是找到了这个属性了重新设置值
-
没有找到这个属性进行 添加这个属性进行赋值操作
总结
-
构造函数的原型prototype
-
实例对象的原型
__proto__
-
实例对象的
__proto__
指向构造函数的prototype -
原型链通过对应的对象的
__proto__
去找对应的属性 直到找到Object为止 -
原型一般上面写函数,可以保证函数只声明一次。对应的属性写在构造函数内
-
原型上的方法/属性。通过实例对象 . 属性名直接访问(平常通过对象去点的方法都称为原型方法)
-
在对应的原型上的函数里面的this指向当前调用的实例对象
通过原型来实现数组的高阶函数
forEach实现 map实现
//数组的原型
//在数组的原型上添加一个myForEach的方法
//在对应的原型上的函数里面的this指向当前调用的实例对象
Array.prototype.myForEach = function(fn){
//遍历
for(let i =0 ;i<this.length;i++){
fn(this[i],this)
}
}
Array.prototype.myMap = function(fn){
let returnArr = []
//遍历
for(let i = 0;i<this.length;i++){
returnArr.push(fn(this[i],i,this))
}
return returnArr
}
reduce实现
//回调函数 defaultValue
Array.prototype.myReduce = function(fn,defaultValue){
//默认请求 前面的值为第一个开始下标为第二个
let previousValue = this[0]
let index = 1
//如果传了初始值 那么对应的初始值为传入的值 开始的下标从0开始
if(typeof defaultValue != 'undefined'){
previousValue = defaultValue
index = 0
}
//遍历
for(let i = index;i<this.length;i++){
previousValue = function(previousvalue,this[i],i,this)
}
return previousValue
}
面向对象的三个特性
封装(函数的抽取 属性的抽取)
继承(子类继承父类)
多态(重写 子类重写父类方法)
继承
概述:
子类继承父类的属性和方法(非私有的属性和方法 非静态的方法)
继承的实现
使用extends关键词实现继承(es6新增 类的继承)
//es6新增类的继承 extends关键词实现
calss Person{
constructor(){
this.name = 'jack'
}
}
class Son extends Person{
constructor(age){
super()
this.age = age
}
}
var son = new Son(18)
console.log(`son`,son);
原型链继承(覆盖之前原型上的所有方式 显示的时候不会显示继承来的属性 (在原型上重复出现一样的属性))
-
核心 在子类的原型上创建父类的对象
function Person(){
this.name = 'jack'
}
function Son(age){
this.age = age
}
Son.prototype.say = ()=>{
console.log('你好');
}
//原型继承
//将要继承的类放在继承的子类的原型上
//原型链会覆盖原本原型上的私有的方法及属性
Son.prototype = new Person()
var son = new Son(18)
console.log(`son`,son);
console.log(son.name);
//son.say()
对象冒充(会显示继承的属性 不能继承原型上的方法)
-
核心 在子类中调用父类的构造函数 更改this指向
function Person(){
this.name = 'jack'
}
function Son(age){
//改this指向 执行父类的构造函数
Person.call(this)
this.age = age
}
var son = new Son(18)
console.log(`son`,son);
组合继承(使用原型链继承和对象冒充结合)
//组合继承 原型链继承加对象冒充
function Person(){
this.name = 'jack'
}
Person.prototype.say = ()=>{
console.log(`hello`);
}
function Son(age){
//改this指向 执行父类的构造函数
Person.call(this)
this.age = age
}
//原型链继承
Son.prototype = new Person()
var son = new Son(18)
console.log(`son`,son);
son.say()
组合寄生继承(对象冒充 + 原型链继承 (创建一个原型对象放在原型链上))
-
对象冒充
-
在子类的原型上创建父类的原型对象
//组合寄生继承
function Person(){
this.name = 'jack'
}
Person.prototype.say = ()=>{
console.log(`hello`);
}
function Son(age){
//改this指向 执行父类的构造函数
Person.call(this)
this.age = age
}
//寄生
Son.prototype = Object.create(Person.prototype)
var son = new Son(18)
console.log(`son`,son);
轮播图面向对象
<!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>
</body>
</html>
<script src='https://cdn.bootcdn.net/ajax/libs/move.js/0.5.0/move.js'></script>
<script>
//需要存储所有的图片的盒子 nav
//焦点 左右切换的盒子
class Carousel{
constructor(box){
this.box = box
//根据大盒子获取存储图片的盒子
this.nav = box.querySelector('.nav')
//根据大盒子获取存储焦点的盒子
this.criList = box.querySelector('.cirList')
//控制的下标
this.index = 0
this.init()
this.handlerClick()
}
init(){
//根据对应的原本的图片个数
//将this.nav.children 当作数组调用forEach方法
Array.prototype.forEach.call(this.nav.children,(v,i)=>{
let htmlCode = '<li></li>'
if(i == 0){
htmlCode = "<li class='selected'></li>"
}
//给对应的焦点进行添加
this.cirList.innerHtML += htmlCode
})
//生成焦点和在对应的nav后添加第一张图
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)`
}
if(this.index > this.nav.children.length - 2 && direction){
this.index = 0
this.nav.style.transform = `translate3d(0px,0px,0px)`
}
//反方向为false
if(direction){
this.index++
}else{
this.index--
}
//移动
var x = this.index * this.box.offsetWidth * -1
this.focusElement(this.index)
move('.nav').to(x,0).duration('1s').end()
}
autMove(){
let _self = this
this.timer = setInterval(()=>{
_self.move(true)
},2000)
}
focusElement(index){
//取值为0-6
if(index == 7){
index = 0
}
//排他思想
Array.prototype.forEach.call(this.cirList.children,(v,i)=>{
v.className = (i == index ? 'selected' : '')
})
}
handlerClick(){
var _self = this
Array.prototype.forEach.call(this.cirList.children,(v,i)=>{
let index = i
v.onclick = function(){
_self.index = index-1
_self.move()
}
})
}
}
var box = document.querySelector('.showBox')
var carousel = new Carousel(box)
carousel.autMove()
</script>
ES6的模块化
模块化的思想,将对应的功能代码(JS代码、CSS代码、HTML代码)封装为一个模块,想要使用别人的就导入,想要给别人用就导出,复用性强。
块模化常用的模式
amd(在加载之前导入)
cmd(在用的时候导入)
comment.js(基于amd和cmd之上)
ES6模块化的关键词(要想导入先要导出)
import 导入
export 导出
export的使用
第一种 export default(只能声明一次)
//默认导出只有一个 如果使用export default 导出的可以使用对应的一个名字来接
export default{
obj,
str,
say
}
接收
//名字为obj(可以任意命名)
import obj from './test.js'
console.log(obj);//{obj,str,say}
第二种导出
//如果直接使用对应的export 导出那么必须通过{键}来接
export const obj = {
name:'jack',
age:18
}
//如果导出的是值类型 一定要写变量名
export const str = '你好世界'
export const say = ()=>{
console.log('hello world');
}
接收
import {obj,str,say} form './test.js'
第三种导出
const name = 'tom'
//第三种导出
export{
//只能写变量
name
}
接收
import {name} form '.test.js'
import使用
import 名字 form '地址'