重新认识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.prototype ➡ Object.prototype ➡ null
.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的方法
Bar.prototpye = Foo.prototype- 只是直接把``Bar.prototpye `浅拷贝
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() // 多多汪
【多多登场】