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
注意事项
-
原型链不包含对象赋值
-
对象赋值的操作是找到这个属性了重新设置值
-
没有找到这个属性进行 添加这个属性进行赋值操作
总结
-
构造函数的原型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],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 = fn(previousValue,this[i],i,this) } return previousValue }
面向对象的三大特性
封装 (函数的抽取 属性的抽取)
继承 (子类继承父类)
多态 (重写 子类重写父类方法)
继承
概述:
子类继承父类的属性和方法(非私有的属性和方法 非静态的方法)
继承的实现
使用extends关键词实现继承(es6新增 类的继承)
// es6新增类的继承 extends关键词实现 class 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);
轮播图面向对象
ES6的模块化
模块的思想,将对应的功能代码封装为一个模块(js代码 css代码 html代码)。
想要使用别人就导入,想要给别人用就导出。复用。
模块化的常用的模式
amd (在对应的加载之前导入)
cmd (在用的时候导入)
comment.js (基于amd和cmd之上)
es6的模块化的关键词(要想导入想要导出)
import 导入
export 导出
export的使用
第一种 export default (只能声明一次)
// //默认导出只有一个 如果使用export default 导出的可以使用对应的一个名字来接 export default { obj, str, say }
接收
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} from './test.js'
第三种导出
const name = 'tom' //第三种导出 export { //只能写变量 name }
接收
import {name} from './test.js'
import使用
import 名字 from '地址'