面向对象的JavaScript

302 阅读5分钟

重新认识javascript

弱类型

动态语言的特点

  • 运行时结构可以被动态修改,eg:定义一个对象,在运行时,还可以添加属性/方法
  • 提供eval() ,将字符串形式传入的代码进行执行
  • 对象的成员(属性、方法),可以被增删改

基于原型的编程语言

  • 基于原型的编程或称为原型程序设计、原型编程,是面向对象程序的子系统和一种方式

  • 在原型编程中,类不是实施的,而且行为重用是通过复制已经存在的原型对象的过程实现的

  • 这个模型一般被认为是classless、面向原型、或者是基于实例的编程

  • 实例&原型 可以互相转换

    `基于类基于原型
    对象构建将对象抽象为类(class)
    以类为规范创建对象实例
    通过构造器(constructor),
    或者原型(prototype)来构建对象
    成员归属状态由对象实例持有
    行为由该对象的类持有
    状态和行为读书与对象本身
    继承机制只有对象的结构、方法能够被继承状态、对象的结构、方法能够被继承

对象的封装&创建

数据类型与内置对象

对象的构造(封装)

  • 用构造函数来实现封装
  • 为了方便使用,为绝大多数内置对象提供了字面式声明方式
//	字面式声明
var Person = {
    name: '张三',
    age: 26,
    eat: function(){
        alert('不吃')
    }
}

// 从对象构造新对象
var p = Object.create(Person)
p.height = 176
// 基于person创建对象,把Person对象指定为p对象的原型,实现了原型关系
// 因为Object.create()不会复制原型链、constructor,此时的p.prototype为 undefined

console.log
	p.__proto__ === Person	// true
	p.contructor === Person  // flase
	p.age	// 26
	p.name	// 张三
	p.prototype  // undefined
	p instanceof Person	// TypeError   
//	因为字面式声明Person不是构造函数,Person是对象,所以这种方法并不推荐,推荐用构造函数👇

构造函数👇

//  构造函数
function Person(name,age){
    /***	为了防止写代码的时候忘了new  手动判断给加上 s  ***/
    if(this instanceof(Person) === false){
        return new Person(name,age)
    }
    /*** END ***/
	this.name = name
    this.age = age
    this.eat = function(){
        alert('不吃')
    }
}
//	通过关键字new 和构造器构造对象,此时将p.__proto__链接到Person.prototype
var p = new Person('张三',26)

console.log
	p.__proto__ === Person.prototype  //  true
	p.name	// 张三
	p instanceof Person	// true
	p.prototype.constructor === Person

关于contructor

function Foo() { ... }
Foo.prototype.constructor === Foo  // true
var a = new Foo()
a.constructor === Foo   // true
  • .constructor引用同样委托给Foo.prototype,而 Foo.prototype.constructor 默认指向Foo
  • a.construcor 只是通过____proto____委托指向 Foo
function Foo() { ... }
Foo.prototype = { ... }   // 创建新原型
var a1 = new Foo()
a1.constructor === Foo   // false
a1.constructor === Object   // true
  • a1没有.constructor ,它委托____ptoto____链上的Foo.prototype
  • 但Foo也没有.constructor ,所以委托到原型链顶端的Object.prototype

原型链

概念

  • JS中,每个对象都有一个指向它的原型(prototype)对象的内部链接(proto
  • 原型对象又有自己的内部链接,指向它的原型,直到链的最后一环(null)
  • 这种一级一级的链结构就成为原型链(prototype chain)
  • 显示原型prototype 实现继承,继承的是prototype属性,不是对象本身
`隐式原型____proto____显示原型prototype
作用实现原型链实现继承
指向原型对象的prototype用于共享给所有实例的对象

ES5

function Foo(){}
var bar = new Foo()
👇
bar.__proto__ === Foo.prototype  // true

ES6

class A {}
class B extends A {}
👇
B.__proto__ === A // true
B.prototype.__proto__ === A.prototype // true

继承和多态的实现

  • 属性的继承
    • js对象是动态的属性‘包’
    • js对象有一个指向原型对象的链接(proto
    • 当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会在对象的原型链上搜索,层层向上,直到找到一个名字匹配的属性或者到达原型链的末尾
  • 方法的继承
    • js并没有其他基于类的语言所定义的‘方法’
    • 在js里,任何函数都可以添加到对象上作为对象的属性。函数的继承与其他的属性继承没有差别县
var numbers = [1,2,3,4,5]
				numbers.__proto__			numbers.__proto__.proto__
numbers		➡	Array.prototypeObject.prototypenull
					.constructor				.constructor
					.length						.length
					.push()						.hasOwnProperty()
					.pull()						.toString()

代码实现

function Animal(){
	this.type = 'animal'
}
function Dog(){
	this.type = 'dog'
}
Dog.prototype = Object.create(Animal.prototype)
Dog.prototype.yell = function() {
    alert("多多叫")
}
var mydoge = new Dog()

console.log
	mydog.type	// dog
	mydog.yell()	// 多多叫
	mydog instanceof Dog	// true
	mydog instanceof Animal	// true
	mydog.constuctor	// Animal    这里并不是Dog,原因是mydog的隐式原型链(__proto__)指向Dog.prototype 没有constuctor 只有yell()

/**
*	需要在Dog.prototype = Object.create(Animal.prototype)增加  👇
**/
Dog.prototypr.constuctor = Dog	// 手动指向contructor为Dog

再次console.log
	mydog.constuctor	// Dog

不推荐 关联prototype的方法

  1. Bar.prototpye = Foo.prototype
    • 只是直接把``Bar.prototpye `浅拷贝
  2. Bar.prototype = new Foo()
    • 能正确创建关联Foo.prototpye
    • 但是,多调用了一次父级,引起父级的副作用

对比两种继承类的方法

面向对象

function Foo(who) {
    this.me = {name: who}
}
Foo.prototype.ide = function () {
    return this.me
}
Foo.prototype.setMe = function (newName) {
     this.me.name = newName
}
function Bar(who) {
    Foo.call(this, who)
}
Bar.prototype = Object.create(Foo.prototype)
let b1 = new Bar('b1')
let b2 = new Bar('b2')
b1.setMe('bbb1')  // 不影响b2

对象关联 (推荐!)

Foo = {
    init: function (who) {
        this.me = {name: who}
    },
    ide: function () {
        return this.me
    },
    setMe: function (newName) {
        this.me.name = newName
    }
}
Bar = Object.create(Foo)
let b1 = Object.create(Bar)
b1.init('b1')
let b2 = Object.create(Bar)
b2.init('b2')

ES6中的面向对象

  • 类:Class

  • 构造函数:constructor

  • Getter 和 Setter

  • 静态关键字:static

    • 表明这是一个静态方法,不会被实例继承,只能通过类来调用
  • 继承关键字:extends

  • 父类关键字:super

ES6的类不是新的创建对象的方法,只是一种“特殊的函数”,因此也包含类表达式和类声明,但是类声明不会被提升

// 类表达式
var Person = class{
    constructor()
    foo()
}

// 类声明
class Person {			// 等价于function Person()
    constructor()
    foo()
}

声明提升

var a = foo()
function foo(){}  // ✔
var b = bar()
var bar = function (){}  // ×


//	ES6 👇
var a = new Foo()
class Foo(){}	// ×
var a = new Bar()
var Bar = class{}	// ×

constructor

//	构造函数
function Person(name,age){
	this.name = name
    this.age = age
    this.eat = function(){
        alert('不吃')
    }
}
Person.prototype = {}
Person.prototype.constructor = Person

//	ES6 👇
class Person{
    //	构造函数
    constructor(name,age){
        this.name = name
    	this.age = age
    }
    eat(){
        alert('不吃')
    }
}

extends

class Animal{
     constructor(name){
        this.name = name
    }
    speak() {
        console.log('动物叨逼叨')
    }
}
class Dog extends Animal{
    speak() {
        console.log(this.name+'汪')
    }
}

const myDog = new Dog('多多')
myDog.speak()	// 	多多汪



【多多登场】

委屈兮兮