JS-面向对象和原型
作者:互联网
面向对象及原型
面向对象核心概念:万物皆对象 (顶层对象Object)抽取行为作为方法 抽取名词作为属性
俩种构建对象的方式
构造函数构建
es6的形式 class
class Person{ constructor(){ //构造器 this.name = 'jack' } }
es3的形式 function
function Person(){ this.name = 'jack' }
使用new关键词
var person = new Person()
-
自动构建对象
-
手动设置属性
-
自动返回对象
工厂构建
function factory(){ var obj = new Object() obj.name = 'jack' return obj }
调用方法
var person = factory()
-
手动构建对象
-
手动设置属性
-
手动返回对象
prototype
概述:所有的函数都拥有一个属性 这个属性称为prototype 他是一个对象空间(里面就可以存放对应的数据)他被称为显式原型
示例
function fn(){ } //打印这个属性对应的空间 console.log(fn.prototype)
-
constructor 构造器他指向对应的构建的函数(指向他自己)
-
根据上面的我们可以看到每个函数都有这样的一个属性,那么作为构造函数他是不是也有对应prototype属性
function Person(){ } console.log(Person.prototype) //作为一个属性存在 属性是唯一 也就是当前他只有一个prototype 属性的声明只会声明一次
-
作为一个属性存在 属性是唯一 也就是当前他只有一个prototype 属性的声明只会声明一次
-
因为当前这个属性 他对应的是一个对象空间,所以他里面的内容可以被更改和设置的
-
那么我们就可以在这个里面存放一些内容
function Person(){ } Person.prototype.username = 'jack' //往这个对象空间存放一个属性 叫username Person.prototype.age = 18 //往这个对象空间存放一个属性 叫age Person.prototype.sayHello = ()=>{} console.log(Person.prototype)
-
通过实例对象 来访问对应的prototype里面的属性
//取出里面数据 因为构造函数是用于构建对象的 所以我们这个prototype的里面数据 是应该给实例对象去获取
var person = new Person() //访问原型中数据(构造函数的prototype里面的属性)实例对象.属性名 console.log(person.username);
-
因为上面讲过 对应的prototype是作为构造函数的属性 而构造函数的他只声明一次 那么对应的prototype也只声明一次
-
也就意味者 你每次拿到的prototype是一个 也就是说他里面存放的数据也只声明一次
var person1 = new Person() console.log(person1.sayHello == person.sayHello);//true
-
由此可见 如果想对应的数据只声明一次的情况 我们可以放在原型里面
总结
-
一般把方法放在原型里面
-
把属性放在构造函数里面
function Son(){ this.name = 'jack' this.age = 18 } Son.prototype.print = function(){ console.log(this); //指向当前的实例对象 console.log(this.name); //指向当前的实例对象 } new Son().print()
__proto__
概述:每个对象都有一个属性 叫做__proto__
,他也是一个内存空间,他指向对应的构造函数的prototype。称为隐式原型。
示例
var obj = {} console.log(obj.__proto__)
这个对象空间里面也可以存储对应的数据 这个__proto__
,他是每个对象都有的,那么实例对象也是个对象他同样也有。
function Person(){ } let person = new Person() person.__proto__.username = 'jack' console.log(person.__proto__)
//通过打印的结果 我们可以看到对应的里面的结果类似于我们的构造的prototype console.log(Person.prototype == person.__proto__) //ture
从上述代码 大家可以看到对应的构造函数的prototype和对应的实例对象的__proto__
是相等,那么也就证明了对应俩个内容其实是一个对象。那么我们就要明白 谁指向谁,从对应的先后顺序来看,我们知道先有的构造函数再有的实例对象,所以对应的实例对象的__proto__
是指向对应的构造函数的prototype。那么这个内容是不是就是对象访问原型的属性(所以他又被称为隐式原型),所以我们在实际使用中并不会用到__proto__
,而是通过这个属性来访问对应的显式原型。
总结
-
实例对象
__proto__
是指向对应构造函数的prototype -
在实际使用中一般不会用
__proto__
,是通过他来访问对应的prototype
从上可得对应的实例对象的__proto__
是指向构造函数的prototype,那么请问对应的函数是不是也是一个对象,他同样具备__proto__
,那么他的__proto__
指向谁,最终又指向谁?
原型链概述: 原型链其实就对应的寻找原型(__proto__
)的过程组成的一个链式结构,称为原型链。
示例
Object.prototype.hello = 'hello' class Person{ constructor(){ this.username = 'jack' } } Person.prototype.age = 18 class Son extends Person{ constructor(){ super() } } Son.prototype.say = ()=>{} let son = new Son() //son的__proto__指向构造函数的原型 对应的Son这个构造函数的__proto__又指向了对应的父类的原型 //对应的父类的构造的函数原型的__proto__指向了对应的Object的构造函数的原型 //那么对应的Object的构造函原型的__proto__ 指向null 这个寻找的过程就称为原型链 console.log(son.__proto__.__proto__.__proto__.__proto__); //那么接下来 我们又知道了对应的对象去访问原型的中属性 直接通过点的形式 //又明白了作用域链 是一层一层向上找变量作用域 //所以我们又可以通过原型链来找对应的属性 原型中的属性 console.log(son.age); //18 console.log(son.hello); //hello console.log(son.abc); //undefined
这个寻找的过程就称为原型链
请问对应的对象的属性赋值算不算原型链?
那么对象的属性赋值的操作有就覆盖 没有就添加
1.找的到就覆盖这个值
2.找不到就添加这个属性
利用原型来实现对应的底层源码
实现对应的数组的高阶函数
//find findIndex Array.prototype.myFind = function(fn){ //先遍历数组 for(var i=0;i<this.length;i++){ if(fn(this[i],i,this)){ return this[i] } } } Array.prototype.myFindIndex = function(fn){ var index = -1 //先遍历数组 for(var i=0;i<this.length;i++){ if(fn(this[i],i,this)){ index = i break } } return index } //map forEach Array.prototype.myMap = function(fn){ var result = [] //先遍历数组 for(var i=0;i<this.length;i++){ result.push(fn(this[i],i,this)) } return result } Array.prototype.myForEach= function(fn){ //先遍历数组 for(var i=0;i<this.length;i++){ fn(this[i],i,this) } }
继承概述:
继承属于面向对象的三大特性之一,面向对象的三大特性。封装 (抽取内容封装) 继承(子类继承父类) 多态(重写 重载)
继承为子类继承父类的内容,子类可以拥有父类所有的非私有的属性及方法。
继承的实现
extends 关键词(class的继承)*(es6的)
class Person{ constructor(){ this.username = 'jack' } } class Son extends Person{ constructor(){ super() } } console.log(new Son().username) //jack 原型继承(prototype赋值给需要继承的对象) function Person(){ this.password = '123' } function Son(){ } Son.prototype = new Person() console.log(new Son().password )//123
组合继承 (原型+call改this指向)
//组合继承 function Person() { this.password = '123' } function Son() { Person.call(this) //将person里面的this改成Son里面的this } Son.prototype = new Person() console.log(new Son().password) //123
对象冒充(改this指向 找不到原型上的内容(无法继承原型))
//对象冒充 function Person() { this.password = '123' } function Son() { Person.call(this) //将person里面的this改成Son里面的this } let son = new Son() console.log(son.password);
多态
概述:一个东西多种形态体现,(水 冰 水汽)。子类是父类多态的体现(基于继承的)。
重写 子类重写父类的方法
function Person(){ this.sayHello = ()=>{ console.log('你好') } } function Son(){ this.sayHello = ()=>{ console.log('hello') //子类重写父类方法 } } Son.prototype = new Person() let son = new Son() son.sayHello() //hello
重载 同一个类里面有多个同名的方法(覆盖 js没有重载)
重写应用
基础拖拽类
class Touch { constructor(ele) { this.ele = ele this.handlerDown() } handlerDown() { this.ele.onmousedown = (e) => { e = e || event //记录的是鼠标在div里面的位置 //在mousedown里面记录按下的位置 this.x = e.offsetX this.y = e.offsetY this.handlerMove() this.handlerUp() } } handlerMove() { document.onmousemove = (e) => { e = e || event //记录每次的位置 在document里面的位置 var currentX = e.pageX var currentY = e.pageY //并且设置div的位置 this.ele.style.left = currentX - this.x + "px" this.ele.style.top = currentY - this.y + "px" } } handlerUp() { //给document添加mouseup事件 document.onmouseup = function () { //清除对应的mousemove事件 document.onmousemove = null } } } var box = document.getElementById('box') new Touch(box)
固定拖拽
class FixedTouch extends Touch { constructor(box, ele) { super(ele) this.box = box } handlerMove() { document.onmousemove = (e) => { e = e || event //记录每次的位置 在document里面的位置 var currentX = e.pageX - getOffset(this.box).left var currentY = e.pageY - getOffset(this.box).top //范围判断 this.target = { x: currentX - this.x, y: currentY - this.y } if (this.target.x < 0) { this.target.x = 0 } if (this.target.y < 0) { this.target.y = 0 } if (this.target.x > this.box.clientWidth - this.ele.offsetWidth) { this.target.x = this.box.clientWidth - this.ele.offsetWidth } if (this.target.y > this.box.clientHeight - this.ele.offsetHeight) { this.target.y = this.box.clientHeight - this.ele.offsetHeight } //并且设置div的位置 this.ele.style.left = this.target.x + "px" this.ele.style.top = this.target.y + "px" } } }
基于固定拖拽的放大镜
class Magnifier extends FixedTouch { constructor(smallBox, bigBox) { //调用父类构造 super(smallBox, smallBox.querySelector('.move')) this.smallBox = smallBox this.bigBox = bigBox this.move = smallBox.querySelector('.move') this.bigImg = bigBox.children[0] this.init() this.handlerHover() //取消按下的事件 this.handlerDown = () => {} //调用move this.handlerMove() } init() { //计算对应的move这个盒子的宽高 // 大的比大的等于小的比小的 bigImg/bigBox = box/move ==> bigImg/box == bigBox/move this.move.style.width = this.smallBox.clientWidth / (this.bigImg.clientWidth / this.bigBox .clientWidth) + 'px' this.move.style.height = this.smallBox.clientHeight / (this.bigImg.clientHeight / this.bigBox .clientHeight) + 'px' //先需要隐藏 this.move.style.display = 'none' this.bigBox.style.display = 'none' } handlerHover() { //移入移出 this.smallBox.onmouseenter = () => { this.move.style.display = 'block' this.bigBox.style.display = 'block' //x y的属性值 this.x = this.move.clientWidth / 2, this.y = this.move.clientHeight / 2 } this.smallBox.onmouseleave = () => { this.move.style.display = 'none' this.bigBox.style.display = 'none' } //添加一个移动的事件 this.smallBox.onmousemove = () => { //改变大盒子里面图片的位置 this.bigImg.style.left = -this.target.x * this.bigImg.clientWidth / this.smallBox.clientWidth + 'px' this.bigImg.style.top = -this.target.y * this.bigImg.clientHeight / this.smallBox.clientHeight + 'px' } } }
es6的模块化
import 导入
export 导出
es6的写法
<script type='module'></script>
一个内容如果需要导入 必须先导出
第一种写法
export导出
//对象 const obj = { username:"jack", password:'123' } //函数 const sayHello = ()=>{ console.log('hello'); } //值 const message = '这个是一个信息' //数组 const arr = [1,2,3,4] //变量的接的数据 需要导出必须用{} export { obj, sayHello, message, arr }
import导入
import { obj, sayHello, message, arr } from './a.js' //解构 sayHello() console.log(`obj`, obj); console.log(`message`, message); console.log(`arr`, arr);
第二种写法
export 导出
export const obj = { username:"jack", password:'123' } export const sayHello = ()=>{ console.log('hello'); } export const arr = [1,2,3,4] export const message = '这个是一个信息'
import 导入
//* 表示所有的 as取别名(随便取) import * as hello from './a.js' hello.sayHello() console.log(`obj`, hello.obj); console.log(`message`, hello.message); console.log(`arr`, hello.arr);
第三种写法
export 导出
export default { obj, sayHello, message, arr }
import 导入
import a from './a.js' a.sayHello() console.log(`obj`, a.obj); console.log(`message`, a.message); console.log(`arr`, a.arr);
总结
export 导出内容
-
export const 对应的变量
-
export default 对应的变量
-
export {}
import 导入内容
-
import 变量名 from 文件地址 (所有的话 * 必须取别名 as)
标签:__,console,log,Person,proto,JS,面向对象,原型,prototype 来源: https://www.cnblogs.com/orangeSky/p/16694578.html