1、创建对象的三种方式
(1)字面量的形式创建对象
var obj = {}
(2)使用new关键字创建对象
var obj = new Object()
(3)使用构造函数的方式创建对象(可以看做创建了一个类)
function Animal(name, age) {
this.name = name
this.age = age
this.bark = function() {
console.log('I like bark')
}
}
var hsq = new Animal('哈士奇', 5)
hsq.bark()
2、构造函数的实例成员和静态成员
(1)实例成员
- 构造函数内部使用this添加的成员就是实例成员
- 实例成员只能通过实例化的对象来访问
function Animal(name) {
this.name = name
}
var hsq = new Animal('哈士奇')
console.log(hsq.name) // 访问实例成员
(2)静态成员
- 在构造函数本身上添加的成员就是静态成员
- 静态成员只能通过构造函数来访问
function Animal(name, age) {
this.name = name
this.age = age
}
Animal.bark = '汪汪汪' // 添加静态成员
var hsq = new Animal('哈士奇', 5)
console.log(Animal.bark) // 构造函数访问静态成员
注意:
1、实例对象访问静态资源,或者构造函数访问实例成员,结果都是undefined
2、在没有类的概念之前,使用构造函数来实现类似功能的
3、构造函数存在内存浪费问题,因为每创建一个实例对象,就会在内存中分配一定的内存空间
3、构造函数的原型(对象)prototype
- 在ES6中,每一个构造函数被创建出来会自动有一个prototype属性(原型对象),不需要自己创建
- 可以为原型对象上面添加属性和方法,这些属性和方法可以被函数创建的实例对象进行调用,可以看做是公共的属性和方法
function Animal(name, age) {
this.name = name
this.age = age
}
// Animal.bark = '汪汪汪' // console.log(hsq.bark) 结果为undefined
Animal.prototype.bark = '汪汪汪'
var hsq = new Animal('哈士奇', 5)
console.log(hsq.bark) // 结果为 汪汪汪
4、对象原型__proto__
对象身上系统会自己添加一个
__proto__
(对象原型)属性,这个属性指向构造函数的原型对象,因此实例对象可以使用原型对象所拥有的方法__proto__
和prototype是等价的-
查找规则:
实例对象要调用一个属性或方法时,先看自己身上有没有,如果有就用自己的,如果没有,会自己通过
__proto__
查找原型对象(prototype)中有没有,如果有的话,就可以使用 一般不去使用
__proto__
修改原型对象所拥有的属性或者方法,强烈不建议
5、constructor构造函数
__proto__
和prototype
中都有一个constructor属性,即构造函数,constructor属性主要记录该对象引用于哪一个构造函数,也可以重新指回构造函数
function Animal(name, age) {
this.name = name
this.age = age
}
Animal.prototype = {
//如果我们修改了原来的原型对象,给原型对象赋值的是一个对象,这样会覆盖掉原型对象中的内容,则必须手动的利用constructor指回原来的构造函数
constructor: Animal,
bark: '汪汪汪',
eat: '吃狗粮'
}
var hsq = new Animal('哈士奇', 5)
console.log(Animal.prototype.constructor)
console.log(hsq.__proto__.constructor)
6、原型链
- 只要是对象就会有
__proto__
原型,指向原型对象 - Animal的对象实例的
__proto__
属性指向Animal的原型对象,Animal原型对象里面的__proto__
指向的是Object原型对象,Object原型对象指向的是null
function Animal(name, age) {
this.name = name
this.age = age
}
var hsq = new Animal('哈士奇', 5)
console.log(Animal.prototype) // {constructor: ƒ}
console.log(Animal.prototype.__proto__ == Object.prototype) // true
console.log(Object.prototype.__proto__) // null
任何对象都有原型对象,也就是prototype属性,任何原型对象也是一个对象,该对象就有
__proto__
属性,这样一层一层往上找,就形成了一条链,即原型链
7、JavaScript成员的查找机制
(1)当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性
(2)如果没有就查找它的原型(也就是__proto__
指向的 prototype 原型对象)
(3)如果还没有就查找原型对象的原型(Object的原型对象)
(4)依此类推一直找到 Object 为止(null)
(5)__proto__
对象原型的意义就在于为对象成员查找机制提供一个方向,或者说一条路线
(6)如果原型链上的对象都有要找的属性(方法),即遵循就近原则,优先使用自己的
8、原型对象中this的指向
(1)在构造函数中,this指向的是实例对象
(2)原型对象函数里面的this,指向的也是实例对象
9、扩展内置对象(对原来的内置对象进行扩展自定义方法)
数组的原型对象中没有求和的方法,可以自定义扩展
只允许以 . 的方式去添加方法,不可以使用对象的形式,不然会覆盖掉原型对象中的所有方法
Array.prototype.sum = function() {
var sum = 0
for (var i = 0; i < this.length; i++) {
sum += this[i]
}
return sum
}
//不允许,会覆盖,内置的原型对象也不允许赋值
// Array.prototype = {
// sum: function() {
// var sum = 0
// for (var i = 0; i < this.length; i++) {
// sum += this[i]
// }
// return sum
// }
// }
var arr = [1, 2, 3, 4]
console.log(arr.sum()) // 10
var arr1 = [11, 22, 33]
congsole.log(arr1.sum()) // 66
10、继承
call(参数1,参数2,…) 参数1为修改后this的指向
1、call( )可以调用函数
2、call( )可以改变this指向
var x = 10
var y = 20
function sum() {
console.log(this.x + this.y)
}
var obj = {
x: 100,
y: 200,
sum: function() {
console.log(this.x + this.y)
}
}
sum() // 30
sum.call(obj) // 300
obj.sum() // 300
obj.sum.call(window) // 30
借用父构造函数实现继承:借用call( )将父构造函数中的this修改为子构造函数的this
function Father(uname, age) {
this.uname = uname
this.age = age
}
function Son(uname, age) {
Father.call(this, uname, age)
}
var son = new Son('zs', 18)
console.log(son) // Son {uname: "zs", age: 18}
借用原型对象实现继承:
本质:子类创建的实例拥有父类创建的实例的属性(使用call( )修改this指向),子类创建的实例可以使用父类的公共方法(必须要产生联系)
- 子构造函数和父构造函数没有任何关系,可以使子原型对象和赋原型对象处于同一条原型链上,这样子原型对象就可以使用父原型对象上的属性和方法了
function Father(uname, age) {
this.uname = uname
this.age = age
}
Father.prototype.hi = function() {
console.log('你好呀')
}
function Son(uname, age) {
Father.call(this, uname, age)
}
// 直接赋值会有问题,如果子原型对象发生变化,父原型对象也会发生变化
// Son.prototype = Father.prototype
// 此时将Son的constructor指向Father,需要重新指回Son的构造函数
Son.prototype = new Father()
Son.prototype.constructor = Son
Son.prototype.exam = function() {
console.log('考试')
}
var son = new Son('zs', 18)
console.log(son)
son.exam()
son.hi()
ES6之前通过
构造函数+原型
实现面向对象编程ES6通过
类
实现面向对象编程类的本质还是一个函数,类是构造函数的另一种表现形式
(1)类有原型对象prototype
(2)类原型对象prototype里面有constructor指向类本身
(3)类可以通过原型对象添加方法
(4)类创建的实例对象有
__proto__原型
指向类的原型对象
11、遍历迭代方法(ES5新增)
(1)数组方法
-
array.forEach(function(currentValue, index, array){})——遍历方法
forEach的参数为一个函数
-
函数的参数:
currentValue:每一项的值
index:每一项的索引
Array:数组本身
-
Array.filter(function(currentValue, index, arr){})——筛选过滤方法(批量筛选)
filter参数是一个函数
-
函数的参数:
currentValue:每一项的值
index:每一项的索引
Array:数组本身
返回值是一个数组,该数组保存遍历过程中执行函数返回值为true的值
var arr = [12, 66, 4, 88] var newArr = arr.filter(function(value, index) { return value >= 20 }) console.log(newArr) // [66, 88]
-
Array.some(function(currentValue, index, arr){})——查找满足条件元素方法(查找某一个)
Some参数是一个函数
-
函数的参数:
currentValue:每一项的值
index:每一项的索引
Array:数组本身
返回值是一个布尔值,遍历过程中,有符合条件的元素,返回true,无符合元素条件,返回false,查找到第一个符合条件的元素就停止查找
[10, 30, 4].some(function(value) { return value >= 20 }) console.log(flag) // true
注意:
在forEach、filter里面return不会终止迭代
在some里面return true就是终止遍历,迭代效率更高
2、字符串方法
-
trim( )去除字符串前后的空白字符
var x = ' abc ' x.trim() // abc(前后无空格) // 可用于输入时的空格检测 console.log(x.length) // 9 console.log(x.trim().length) // 9 // trim()只会去掉字符串两边的空格,并不会改变它的值
3、对象方法
-
Object.keys(obj) 获取对象的属性名
效果类似于for…in
将所有的属性名获取到之后保存在一个数组中
可用于判断对象中是否有某个属性
var obj = { id: 1, name: 'zs' } var arr = Object.keys(obj) console.log(arr) // (2)['id', 'name']
-
Object.defineProperty(obj, prop, descriptor) 定义新属性或修改原有的属性
obj:目标对象
prop:属性名
descriptor: 描述
value:属性已有值则修改,没有属性则赋值,默认为undefined writable:布尔值,目标属性是否允许修改,默认为false enumerable:布尔值,目标属性是否可以被遍历,默认为false configurable:不允许被删除或不允许再次修改第三个属性的特性,默认为false
var obj = { id: 1, name: 'zs' } Object.defineProperty(obj, 'address', { value: '陕西西安', writable: false, enumerable: false, // 无法被遍历 configurable: false }) console.log(obj) console.log(Object.keys(obj)) delete obj.address // 无法被删除 console.log(obj)