拿起我前端必备的经久耐用的javascript高级程序 再认真的理解一次原型链以及面向对象编程的基础知识 熟悉面向对象编程呢 会用和完全融会贯通是有区别的 好好看看基础的概念理解;
理解对象
1. 创建自定义对象
// Object构造函数
var person = new Object()
person.name = 'Jamie'
person.age = 18
person.sayName = function () {
alert this.name
}
// 对象字面量
var person = {
name: 'Jamie'
age: 18,
sayName: function() {
alert(this.name)
}
}
2 属性(property)类型
数据属性 和 访问器属性
数据属性 的四个描述特征
configable 能否都通过delete删除 修改属性的特征 能否修改为访问器属性
enumerable 是否可以枚举 能否通过for-in循环返回
writable 是否可以修改属性的值
value 属性的值
如果一个对象已经定义name 如
//定义了一个对象 ,它的configable、enumerable、whiteable默认值为true, value被赋值为 Jamie
var person = {
name: 'Jamie'
}
Object.defineProperty(person, 'name', {
whitable: false
})
person.name = 'wen'
console.log(person.name) //Jamie 不可修改属性的值 所以不变
值得注意是是当把对象的configable设置为 false的话 则不能修改属性的特征 所以再之后就不能操作修改为其他。 用Object.defaineProperty()创建新属性时,不指定的话 configable、enumerable、whiteab默认为false(一般不这么用)
访问器属性
可以对数据进行一些处理,特性包括 configable、enumerable /
get/set
访问器属性不能直接定义 只能用Object.defineProperty()定义
var book = {
_year: 2004,
edition: 2
}
Object.defineProperty(book, "year", {
get: function() {
return this._year
},
set: function(value){
if(value > 2004) {
this._yearn = value
this.edition = value -2004
}
}
})
常用用法: 设置一个的值 导致另一个值跟随变化
Object.defineProperties()给对象定义多个属性
3.读取属性的特性 Object.getOwnPropertyDesctiptor()
创建对象
工厂模式
function createPerson (name, age, job) {
var o = new Object()
o.name = name
o.age =age
o.job =job
return o
}
var perspn = createPerson('Jamie', 18, 'banzhuan')
构造函数模式
function Person (name, age, job) {
this.name = name
this.age = age
this.job = job
this.sayName = function() {
alert(this.name)
}
}
var person1 = new Person('Jamie', 18, 'banzhuan')
构造函数都始终都应该以一个大写字母开头
创建构造函数必须使用new操作符,以这种方式调用构造函数会经历4个步骤
- 创建一个新对象
- 将构造函数的作用域赋值给构造函数的实例
- 执行构造函数中的代码给实例添加属性
- return新对象
实例都有一个constructor(构造函数属性)指向它的构造函数
person1.construct === Person 】
以这种方式定义的构造函数是定义在Gloabal对象上的
将构造函数当作函数
构造函数也是函数 唯一的区别是 调用方式不同, 构造函数通过new操作符调用
构造函数的问题
缺点: 每个方法都要在每个实例上重新创建一遍,原因在于 函数也是对象 我们添加一个function方法时 实质上是调用了new Function() 实例化了构造函数 Function的一个对象
是,所以不同实例上的同名函数是不一样的
原型模式
首先要明白的一点是: 每个函数都有一个原型属性(prototype), 这是个指针,指向一个对象,这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法(也就是person的原型对象是Person.prototype指向的对象)
function Person () {}
Person.prototype.name = ‘Jamie'
~~~
var person1 = new Person()
终于要说到 原型对象了 (激动)
只要创建一个新函数就会为它创建一个prototype属性,指向它的原型对象,所有原型对象会自动获取一个constructor(构造函数)属性,这个属性是一个指向prototype属性所在函数的指针。
用前面的例子说明 Person函数创建的时候 会有一个prototype的属性(Person.prototype)这个属性指向原型对象,原型对象有一个contractor的构造函数属性,又指向拥有prototype属性的Person函数。
创建实例对象person1后,person1 包含一个指针(proto)指向它的构造函数(Person)的原型对象(Person.prototype)
可以看出实例与构造函数的原型对象存在这样的关系,而不是存在于实例与构造函数之间。
isPrototypeOf() 方法用于测试一个对象是否存在于另一个对象的原型链上。(在js中 万物皆对象)
Person.prototyepe.isPrptotypeOf(person) // true
Object.getPrototyprOf() 方法返回指定对象的原型(内部[[Prototype]]属性的值
function Person () {
}
Person.prototype.name='Jamie'
Person.prototype.sayName = function () {
alert(this.name)
}
var person = new Person()
Person.prototype.isPrototypeOf(person) //true
Object.getPrototypeOf(person) // {name: "Jamie", sayName: ƒ, constructor: ƒ}
Person.prototype // {name: "Jamie", sayName: ƒ, constructor: ƒ}
Person.prototype === Object.getPrototypeOf(person) // true
查找对象属性: 读取对象属性时会执行一次搜索,首先在对象实例中搜索,找到返回,找不到继续搜索指针指向的原型对象。
为对象实例添加属性时,如果和原型对象上的属性名字相同,就屏蔽原型对象上的属性。并不能通过实例修改原型对象。使用delete操作符可以完全删除实例属性。
hasOwnProperty指示对象自身属性中是否具有指定的属性(也就是,是否有指定的键)可以检测来自实例还是原型对象。
无论属性在实例还是 原型上 通过 属性 in 实例 都返回true
function Person () {}
Person.prototype.name = 'Jamie'
Person.prototype.job = 'codding'
Person.prototype.sayName = function () {
alert(this.name)
}
var person1 = new Person()
var person2 = new Person()
person1.hasOwnProperty('name') // false 实例上没有 在原型上
person1.name = 'wen'
person1.hasOwnProperty('name') // true
"name" in person1 // true
"name" in person2 // true
如果通过in 返回true 而通过hasOwnProperty返回false说明在原型上
返回以个对象的所有属性
所有可枚举属性: Object.keys()
所有属性(包括可枚举的属性): Object.getOwnPropertyNames()
更简单的原型语法
function Person () {}
Person.prototype = {
name: 'jamie',
age: 18,
job: 'coding',
sayName: function(){
alert(this.name)
}
}
Object.getOwnPropertyNames(Person.prototype) // ["name", "age", "job", "sayName"]
function Hen () {}
Hen.prototype.index = 1
Hen.prototype.name = 'wendy'
Object.getOwnPropertyNames(Hen.prototype) // ["constructor", "index", "name"]
var friend = new Person()
console.log(friend.contructor === Person) // false
上面的代码将Person.prototype 设置为一个等于一个对象字面量形式创建的新对象,相当于重写了Person的prototype对象,但是会导致失去constructor(创建一个函数 就会自动创建它的prototype对象, 这个对象自动获得它的contructor)
如果contrucor的值真的很重要 我们可以通过以下方式
缺点: 会导致它的可枚举特性设置为true
function Person () {}
Person.prototype = {
name: 'shuang',
age: 18,
contructor: Person
}
因为默认情况 为不可枚举的 我们可以通过 Object.defineProperty()
function Person () {}
Person.prototype = {
name: 'shuang',
age: 18
}
Object.defineProperty(Person.prototype, contructor, {
enumberable: false,
value: Person
})
原型对象的问题
- 省略了为构造函数传参数的步骤,所有示例初始化都拥有同样的属性值
- 如果属性中包含了引用类型的值,就会导致 我们通过实例一 修改了原型对象的值 在实例而值 也有体现
function Students () {}
Students.prototype = {
constructot: Srudents,
name: 'xuesheng',
age: 18,
hobby: ['读书']
}
let student1 = new Students()
let student2 = new Students()
student1.hobby.push('打游戏')
console.log(student1.hobby)// ["读书", "打游戏"]
console.log(student2.hobby)//["读书", "打游戏"]
组合使构造函数模式和原型模式(常用方式 是用来定义引用类型的默认模式)
说明: 构造函数用于定义实例属性,原型模式用于定义方法和共享属性 这样就解决了上面的问题
function Person (name, age) {
this.name = name
this.age = age
this.hobby = ['coding']
}
Person.prototyoe = {
contructor: Person,
sayName: function() {
alert(this.name)
}
}
var person1 = new Person('shuang', 18)
var person2 = new Person('jie', 28)
person1.hobby.push('gaming')
console.log(person1.hobby)
console.log(person2.hobby)