原型
原型的作用:
- 通过引用对象的属性key来获取一个value时,它会触发
[[Get]]的操作 - 如果我在自己的属性里面没找到,就会去原型上面去找
- 对象的原型
var obj = {
name: 'www',
age: 19
}
// 获取obj原型
console.log(obj.__proto__); // 浏览器实现的(非标准的)
console.log(Object.getPrototypeOf(obj)); // (标准的)
- 函数的原型 prototype
function foo() {}
var obj = {}
// 1. 构造函数看作是一个普通的对象时,他是具备__proto__(隐式原型)
Object.prototype(foo.__proto__)
Object.prototype(obj.__proto__)
// 2. 将函数看作一个函数时,他是具备prototype(显示原型),对象没有这个原型
console.log(foo.prototype);
- new操作符
当我们多个对象拥有共同的值时,可以将它放到构造函数对象的显示原型
由构造函数创建出来的所有对象,都会共享这些属性
function Student(name) {
this.name = name
}
Student.prototype.running = function () {
console.log(this.name + 'running');
}
let stu1 = new Student('wqq', 22)
let stu2 = new Student('qq', 22)
stu1.running()
stu2.running()
当通过new关键字去调用这个构造函数,会做一下操作:
- 创建一个空对象
- 将this指向这个空对象
将Foo的prototype(显示原型)赋值给空对象的__proto__(隐式原型)- 执行代码块
- 返回这个对象
function Foo() {}
console.log(Foo.prototype);
var fn = new Foo()
console.log(fn.__proto__ === Foo.prototype); // true
- constructor属性
默认情况下原型上都会添加一个属性叫做
constructor,这个constructor指向当前的函数对象
function Person() {}
var personPrototype = Person.prototype // 原型
console.log(personPrototype.constructor === Person); // true
- 重写函数原型对象
function Person() {}
// 重写原型对象,它没有constructor
Person.prototype = {
message:'hello',
name:'wqq',
constructor:Person // 会做这个操作
}
// 更加精准的做法
Object.defineProperty(Person.prototype,'constructor',{
enumerable:false,
configurable:true,
writable:true,
value:Person
})
var p1 = new Person()
console.log(p1.message); // hello
继承
{ } 的本质
let obj = {}
// 相当于
let obj2 = new Object()
console.log(obj.__proto__ === Object.prototype);
原型链实现继承
function Person(name,age) {
this.name = name
this.age = age
}
Person.prototype.running = function () {
console.log('Person running');
}
function Student(name, age, height) {
this.name = name
this.age = age
this.height = height
}
Student.prototype.studying = function () {
console.log('Student studing');
}
方式一:父类的原型直接赋值给子类的原型,Student.prototype = Person.prototype
缺点:共享同一个原型,修改任意一个,另一个也会被修改
Student.prototype = Person.prototype
let p1 = new Person()
p1.studying() // 父类能调用子类的方法
方式二:创建一个父类实例对象 ( new Person() ) ,用这个实例对象作为子类的原型
缺点:不能给Person传递参数
var p = new Person('qqq',19)
Student.prototype = p
let p1 = new Person()
p1.studying() // 父类不能调用子类的方法
// 创建学生
var stu1 = new Student('www',10,1.66)
stu1.running() // Person running
stu1.studying() // Student studying
console.log(stu1.name, stu1.age); // www 10
借用构造函数继承
在子类型构造函数的内部调用父类型构造函数
/* 缺点:
* 1. 是无论在什么情况下,都会调用两次父类构造函数(创建子类原型、子类实例的时候)
* 2. 父类型中的属性会有两份: 一份在原型对象中, 一份在子类型实例中
* */
function Person(name,age) {
this.name = name
this.age = age
}
Person.prototype.running = function () {
console.log('Person running');
}
function Student(name, age, height) {
// 借用构造函数,this:Student
Person.call(this,name,age)
this.height = height
}
var p = new Person('qqq',19)
Student.prototype = p
Student.prototype.studying = function () {
console.log('Student studying');
}
// 创建学生
var stu1 = new Student('www',10,1.66)
stu1.running() // Person running
stu1.studying() // Student studying
console.log(stu1.name, stu1.age); // www 10
寄生组合式继承函数
创建一个封装继承过程的函数, 该函数在内部以某种方式来增强对象,最后再将这个对象返回
// 工具函数,创建对象的过程(没有兼容问题)
function createObject(o) {
function F() {}
F.prototype = o
return new F()
}
// 将SubType和SuperType联系到一起(寄生式函数)
function inherit(SubType, SuperType) {
SubType.prototype = createObject(SuperType.prototype)
Object.defineProperty(SubType.prototype, 'constructor', {
enumerable: false,
configurable: false,
writable: true,
value: SubType
})
}
function Person(name,age) {
this.name = name
this.age = age
}
Person.prototype.running = function () {
console.log('running');
}
function Student(name,age,height,sno) {
Person.call(this,name,age)
this.height = height
this.sno = sno
}
inherit(Student, Person) // Student 继承自 Person
Student.prototype.studying = function () {
console.log('studying');
}
var stu1 = new Student('www',19,1.99,100)
stu1.running()
Object类是所有对象的父类
Object.prototype.message = '吃点香菜'
class定义类
class定义的类不能通过普通的函数进行调用
// es6定义类
class Person {
// 通过new调用Person类时,默认会调用这个类的构造函数constructor
constructor(name,age) {
this.name = name
this.age = age
}
// 实例方法,本质上是放在prototype上的
running(){
console.log('running');
}
eating(){
console.log(this.name, 'eating');
}
}
// 创建实例对象
var p1 = new Person('www',20)
console.log(Person.prototype === p1.__proto__); // true
p1.eating() // www eating
访问器方法
// class定义类
class Person {
constructor(name,age) {
this._name = name
this.age = age
}
// 访问器的编写方式
set name(value) {
console.log('设置name');
this._name = value
}
get name() {
console.log('获取name');
return this._name
}
}
var p1 = new Person('qq',20)
应用场景:
// class定义类
class Rectangle {
constructor(x, y, w, h) {
this.x = x
this.y = y
this.w = w
this.h = h
}
get position() {
return {x: this.x, y: this.y}
}
get size(){
return {w:this.w,h:this.h}
}
}
var p1 = new Rectangle(10, 20, 100, 200)
console.log(p1.position); // {x: 10, y: 20}
console.log(p1.size); // {w: 100, h: 200}
类的静态方法
class Person {
// 实例方法
runing() {
console.log(this); // p
console.log('running');
}
// 类方法(静态方法)
static eating() {
console.log(this); // Person
console.log('eating');
}
}
var p = new Person()
p.runing() // running
Person.eating() // eating
ES6实现继承
// 定义父类
class Person{
constructor(name,age) {
this.name = name
this.age = age
}
running(){
console.log('running');
}
}
class Student extends Person{
constructor(name,age,sno,score) {
super(name,age)
this.sno = sno
this.score = score
}
}
class Teacher extends Person{
constructor(name,age,title) {
super(name,age)
this.title = title
}
}
var stu1 = new Student('ww',100)
stu1.running() // running
super关键字
在子(派生)类的构造函数中使用this或者返回默认对象之前,必须先通过super调用父类的构造函数
class Animal{
running(){
console.log('running');
}
static sleep(){
console.log('animal sleep');
}
}
class Dog extends Animal{
// 子类对继承过来的方法不满意,可以重写
running() {
console.log('dog四条腿running');
super.running(); // 调用父类的方法
}
static sleep(){
console.log('趴着');
super.sleep()
}
}
var dog = new Dog()
dog.running()
Dog.sleep()
类的混入mixin
js只支持单继承(不支持多继承)
function mixinAnimal(BaseClass) {
return class extends BaseClass{
running(){
console.log('running');
}
}
}
function mixinRunning(BaseClass) {
return class extends BaseClass{
flying(){
console.log('flying');
}
}
}
class Bird {}
// var NewBird = mixinRunning(mixinAnimal(Bird))
class NewBird extends mixinRunning(mixinAnimal(Bird)){}
var bird = new NewBird()
bird.running()
bird.flying()
多态
面向对象的三大特性:封装、继承、多态
不同的数据类型进行同一个操作,表现出不同的行为,就是多态的体现
// 继承是多态的前提
function sum(a, b) {
console.log(a + b);
}
sum(10,20)
sum('abc','efg')
字面量的增强
var name = 'www'
var age = 19
var key = 'address'
var obj = {
// 1. 属性增强
name,
age,
// 2. 方法增强
eating:function () {
console.log(this); // obj
},
swimming:()=>{
console.log(this); // window
},
running(){
console.log(this); // obj
},
// 3. 计算属性名的增强
[key]:'成都'
}
obj.eating()
obj.swimming()
obj.running()
function foo() {
var message = 'hello'
var info = 'wqq'
return {message,info}
}
解构
// 数组的解构,有严格的顺序要求
var arr = ['aa', 'bb', undefined, 'cc', 'dd']
var [a, b] = arr
console.log(a, b); // aa bb
var [a, , b] = arr
console.log(a, b); // aa cc
var [a, b, ...c] = arr
console.log(a, b, c); // aa bb ['cc','dd']
var [a, b, c = 'default'] = arr
console.log(a, b, c); // aa bb default
// 对象的解构(没有顺序,根据key来解构)
var obj = {name: 'www', age: 19, height: 1.66}
var {name, age, height} = obj
console.log(name, age, height); // www 19 1.66
var {height:h,name} = obj
console.log(h, name); // 1.66 'www'
var {name,age,add='默认值'} = obj
console.log(name, age, add); // www 19 默认值