javascript对象与面向对象

对象是javascript的复合数据类型。对象可看做是属性的无序集合,每个属性都是一个名/值对,属性名是任意字符串,但对象中不能存在两个同名的属性,值可以是任意类型。面向过程思维方式: 把解决问题的关注点放在每一个步骤上。面向对象思维方式: 把解决问题的关键点放在解决问题需要的一系列对象上。

对象的特点

  • 属性的值(value)
  • 可写(writable) 表明是否可以设置该属性的值
  • 可枚举(enumerable) 表明是否可以通过for/in或for/of遍历该属性
  • 可配置(configurable) 表明是否可以删除或修改该属性
  • 对象的原型(prototype) 指向另外一个对象,本对象的属性继承自它的原型对象
  • 对象的类(class) 是一个标识对象类型的字符串

创建对象

  1. 对象直接量(对象字面量)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    var point = {x:0, y:1}
    var book = {
    'title': 'Javascript',
    'sub-title': 'js',
    'author': {
    firstname: 'Bob',
    surname: 'David'
    }
    }
  2. 通过new 构造函数()

    1
    2
    3
    var obj = new Object({x:1}) // 创建一个对象 同 {x:1}
    var arr = new Array(1,2) // 创建一个数组 同 [1, 2]
    var d = new Date() // 创建一个表示当前时间的date对象
  3. Object.create() 创建原型对象
    Object.create()是一个静态函数,第一个参数是这个对象的原型,第二个参数为可选参数,用以对对象的属性进行进一步的描述

    1
    2
    var P = {name: 'tew'}
    var o3 = Object.create(P)

new 构造函数() 经历四个步骤

  • 创建一个新对象
  • 将构造函数的作用域赋值给新对象(this指向新对象)
  • 执行构造函数中的代码(为新对象this添加属性)
  • 返回新对象(默认是 return this)

获取和设置对象的属性

可以通过点(.)和方括号([])运算符来获取属性的值

1
2
3
4
5
6
7
8
9
10
11
var book = {
'title': 'Javascript',
'sub-title': 'js',
'author': {
firstname: 'Bob',
surname: 'David'
}
}
console.log(book.title) // 得到book的title属性
console.log(book['sub-title']) // 得到book的sub-title属性
book.title = 'jquery' // 设置或修改book的title属性

对象的方法

  • toString()方法 将对象转换为字符串

    1
    2
    s = {x:1, y:2}.toString();
    console.log(s) // "[object Object]"
  • toLocalString()方法 返回对象的本地化字符串

    1
    2
    3
    4
    5
    6
    var local = new Date().toLocaleString()
    console.log(local) // "2017/12/26 上午14:30:14"
    var date = new Date().toLocaleDateString()
    console.log(date) // "2017/12/26"
    var time = new Date().toLocaleTimeString()
    console.log(time) // "上午14:30:14"

面向对象

  • 面向对象概念: 用对象的思想去写代码,就是面向对象编程
  • 面向对象特点:
    1. 抽象:抓住核心问题
    2. 封装:只能通过对象来访问方法 将一些属性和方法封装起来
    3. 继承:从已有对象上继承出新的对象
    4. 多态:多对象的不同形态,父类的成员可以共享给所有子类,子类可以给父类添加属性
  • 对象:带有属性和方法的特殊数据类型
    • 对象下面的变量 叫做对象的属性 状态,静态的
    • 对象下面的函数 叫做对象的方法(行为,操作) 过程,动态的

创建对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
var obj = new Object()
// 为对象添加属性和方法
obj.name = "tew"
obj.showName = function(){
console.log(this.name)
}
obj.showName() //tew
// 工厂方式: 封装函数
function Person(name){
//原料 空对象
var obj = new Object()
//加工
obj.name = name
obj.showName = function(){
console.log(this.name)
}
//出厂
return obj
}
var p1 = Person('tew')
p1.showName() // tew
var p2 = Person('cj') //cj
p2.showName()
// 当new去调用一个函数: 这个时候函数中的this就是创建出来的对象,而且函数的返回值就是this对象(隐式返回)
// 构造函数 new 后面调用的函数 (构造函数名第一个字母大写)
function Person(name){
this.name = name
this.showName = function(){
console.log(this.name)
}
}
var p1 = new Person('tew')
p1.showName() // tew
// 对象的引用
// 基本类型: 赋值的时候只是值得复制 在内存中重新开辟空间
// 对象类型: 赋值不仅是值得复制,而且是引用的传递
var a = [1, 2, 3] //两者地址绑在一起
var b = a
b.push(4)
console.log(a) //1,2,3,4 对象进行比较的时候,值和引用都要相同
var a = [1,2,3]
var b = a
b = [1,2,3,4] //重新复制地址改变
console.log(a) //1,2,3

// 原型:去改写对象下面公用的方法或属性,让公用的方法或者属性在内存中存在一份(提高性能)
// 原则: 相同的属性和方法可以加载到原型上 自己和原型中都有的属性或方法,优先用自己的
// 原型方法或属性 相当于css中的class 多个公用
// 普通方法或属性 相当于css中的style
var arr = [1,2,3,4,5]
var arr2 = [1,2,3,4]
arr.sum = function(){
var res = 0
for(var i=0;i<this.length;i++){
res+=this[i]
}
return res
}
console.log(arr.sum()) //15
//arr2.sum();//arr2.sum is not a function
arr2.sum = function(){
var res = 0
for(var i=0;i<this.length;i++){
res+=this[i]
}
return res
}
console.log(arr2.sum()) //10
//原型写法prototype 要写在构造函数下面 只有构造函数才能有原型
Array.prototype.sum = function(){
var res = 0
for(var i=0;i<this.length;i++){
res+=this[i]
}
return res
}
console.log(arr.sum()) //15
console.log(arr2.sum()) //10

更改this指向

  1. 将this用全局变量存起来
  2. 使用call方法改变this指向 fn.call(参数1,参数2,参数3) 参数1 可以改变函数执行过程中的this指向 参数2以后就是执行函数的参数
  3. 使用apply方法改变this指向 fn.apply(this(要修改的那个对象),[参数1,参数2])
    注: 使用call和apply传入的第一个参数可以是null,表示为函数调用模式,this指向window当第一个参数为值类型的时候,会将值类型转换为对应的对象

包装对象

系统对象也是基于原型的程序 尽量不要去修改或者添加系统对象下面的方法和属性
所有的基本类型都有自己的包装对象: Number String Boolean
基础类型在执行方法或者属性的时候,会找到对应的包装对象

1
2
3
4
5
6
7
8
9
10
11
12
13
var a = new String('abc')   //console.log(typeof a)
// 通过包装对象去执行方法或者属性
a.charAt(0)
// 基本类型的包装对象 只存在于一行代码的执行瞬间 执行完后,包装对象立即被销毁
var str = 'abc'
str.num = 10
console.log(str.num) //undefined
// 基本数据类型: 基于字面量方式创建出来的值是基本类型值 number
// 基于构造函数方式创建出来的值是引用类型 object
var num1 = 12
var num2 = new Number(12)
console.log(typeof num1) // number
console.log(typeof num2) // object

原型和原型链: 实例对象和原型之间的链接 proto

原型里面默认的属性和方法 不能for in打印
原型链的最外层是 Object.prototype

  • instanceof 检测当前实例是否属于这个类

    1
    2
    3
    4
    let arr = []
    console.log(arr instanceof Array) // true
    console.log(arr instanceof RegExp) // false
    console.log(arr instanceof Object) // true
  • in 检测当前对象是否存在某个属性(对象的私有属性或共有属性,只要有结果就是true)

    1
    2
    3
    4
    5
    6
    7
    function Fn() {
    this.m = 10
    }
    var f = new Fn()
    console.log('m' in f) // true
    console.log('n' in f) // false
    console.log('toString' in f) // true
  • hasOwnProperty() 检测当前属性是否为对象的私有属性(不仅要有这个属性,还必须是私有的)

    1
    2
    3
    4
    5
    6
    7
    function Fn() {
    this.m = 10
    }
    var f = new Fn()
    console.log(f.hasOwnProperty('m')) // true
    console.log(f.hasOwnProperty('n')) // false
    console.log(f.hasOwnProperty('toString')) // false 不是私有属性
  • Object.prototype.hasOwnPeroperty
    对象中是否有某属性,返回布尔值
    原型如果改成json写法 会影响这个属性

  • constructor
    对象.constructor 查看对象的构造函数
    构造函数:原型里面默认的属性
    每一个函数都会有的,都是自动生成的 Aaa.prototype.constructor = Aaa;
    原型如果改成json写法 会影响这个属性
  • 原型查找原则
    1.当访问一个对象的成员的时候,先在自身找有没有,如果有,直接使用
    2.如果没有找到,则去当前对象的原型对象中去找,如果有,直接使用
    3.如果没有找到,则去原型对象的原型对象中去找,如果有,直接使用
    4.直到Object,如果还是没有,则返回null

  • 构造函数显式原型prototype上的constructor属性等于构造函数本身
    Person.prototype.constructor===Person) //true

  • 实例对象的隐式原型等于它构造函数的显示原型
    person.__proto__===Person.prototype //true
    原型链
  • es6中操纵原型的内置函数
  1. Object.create 根据指定的原型创建新对象, 原型课时是null
  2. Obejct.getPrototypeOf() 获取一个对象的原型
  3. Object.setPrototypeOf() 设置一个对象的原型
  • 构造函数模式用于定义实例属性,而原型模式用于定义共用方法和共享的属性。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
//构造函数
function Foo(name,age){
this.name = name
this.age = age
//return this //默认有着一行
}
var f = new Foo('wangwu',20);
//构造函数--扩展
/*
var a = {} 其实就是 var a = new Object()的语法糖
var a = [] 其实就是 var a = new Array()的语法糖
function Foo(){} 其实就是 var Foo = new Function()
*/
//5条原型规则 原型规则是学习原型链的基础
//a. 所有的引用类型(数组 对象 函数) 都具有对象特性 即可自由扩展属性(null除外)
//b. 所有的引用类型(数组 对象 函数) 都有一个__proto__属性, 属性值是一个普通对象(null除外)(隐式原型)
//c. 所有的函数,都有一个prototype属性,属性值也是一个普通的对象(显示原型)
//d. 所有的引用类型(数组 对象 函数),__proto__属性值指向它的构造函数'prototype'属性值
//e. 当试图得到一个对象(引用类型)的某个属性时,如果这个对象(引用类型) 本身没有这个属性,那么会去它的__proto__(即它的构造函数的prototype)中寻找
{
//1. 都有可自由扩展属性
var obj = {}; obj.a = 100;
var arr = []; arr.a = 100;
function fn(){}; fn.a = 100;
//2. 所有的引用对象都有 __ptoto__属性 隐式原型属性 指向其构造函数
console.log(obj.__proto__); // {constructor: f...} Object
console.log(arr.__proto__); // [constructor: ƒ...] Object
console.log(fn.__proto__); // ƒ () { [native code] }
//3. 有的函数都有一个prototype属性 显示原型属性
console.log(fn.prototype); // {constructor: ƒ}
//4. 所有的引用类型的__proto__ 属性值都指向它的构造函数'prototype'属性值
console.log(obj.__proto__===Object.prototype); // true
//构造函数
function Foo(name){
this.name = name;
}
//原型扩展
Foo.prototype.alertName = function(){
console.log(this.name)
}
//创建实例
var f = new Foo('zhangsan');
f.printName = function(){
console.log(this.name)
}
//测试
f.printName(); //zhangsan f 本身的属性方法
f.alertName(); //zhangsan f 的显示原型去找 prototype中去找
f.__proto__ // {alertName: ƒ, constructor: ƒ}
f.__proto__.__proto__ // { constructor: ƒ... } Object
f.toString(); //"[object Object]" 要去 f.__proto__.__proto__去找
}
// 调用构造函数时会为实例添加一个指向最初原型的prototype指针
// 如果重写整个原型对象(即将原型修改为另一个对象) 就等于切断了构造函数与最初原型之间的联系
// 实例中的指针仅指向原型 而不指向构造函数 实例仍然引用的是最初的原型
function Person() {}
var person = new Person()

// Person.prototype.name = 'tew'
// Person.prototype.sayName = function () {
// console.log(this.name)
// }
// person.name // 'tew'
// person.sayName() // tew

Person.prototype = {
constructor: Person,
name: 'tew',
sayName: function () {
console.log(this.name)
}
}
// person.name // undefined
// person.sayName() // Uncaught TypeError: person.sayName is not a function

继承

一个对象没有一些方法和属性,而把另一个对象的属性和方法,拿过来自己用就是继承

  • 子对象可以继承父对象
    子对象拥有父对象有的东西
    子对象可以扩展
    子对象的扩展不会影响到父对象
  • 继承的方式
    (1) 混入式继承
    (2) 原型继承
    (3) 经典继承
    (4) 原型链继承

  • js对象的默认继承是原型继承

    1
    2
    3
    function Phone{}
    Phone.prototype = new Object() // 默认就会有这一步
    Phone.prototype.__proto__ === Object.prototype
  • 继承的方式 原型链继承 借用构造函数继承 原型+构造函数继承 寄生式继承

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    // 1. 原型链继承
    function Animal () {
    this.age = 20
    }
    function Cat () {
    this.name = 'jack'
    }
    var cat = new Cat()
    console.log(cat.name); // jaack
    console.log(cat.age); // undefined
    // 让Cat对象拥有了Animal对象的属性和方法
    Cat.prototype = new Animal()
    var cat = new Cat()
    console.log(cat.name); // jaack
    console.log(cat.age); // 20

    // 2. 借用构造函数继承 在子类构造函数内部调用父类构造函数
    function Animal () {
    this.age = 20
    }
    function Cat () {
    // Cat的所有对象借用了Animal对象的构造函数 借用构造函数可以传参
    // Animal.call(this, '30')
    Animal.call(this)
    this.name = 'jack'
    }
    var cat = new Cat()
    console.log(cat.name); // jaack
    console.log(cat.age); // 20

    // 3. 组合继承 原型链 + 借用构造函数继承
    // 使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承
    function SuperType(name){
    this.name = name
    this.colors = ['blue', 'green']
    }
    SuperType.prototype.sayName = function(){
    console.log(this.name)
    }
    function SubType(name, age){
    // 继承属性
    SuperType.call(this, name)
    this.age = age
    }
    // 继承方法
    SubType.prototype = new SuperType()
    SubType.prototype.sayAge = function(){
    console.log(this.age)
    }

    var sub = new SubType('tew', 28)
    sub.colors.push('red')
    console.log(sub.colors) // ["blue", "green", "red"]
    sub.sayName() // tew
    sub.sayAge() // 28

    var sub2 = new SubType('bob', 26)
    console.log(sub2.colors) // ["blue", "green"]
    sub2.sayName() // bob
    sub2.sayAge() // 26
    // 4. 寄生式继承
    function Person(name, age){
    this.name = name
    this.age = age
    }
    function createPerson(name, age){
    var obj = {}
    Person.call(obj, name, age)
    return obj
    }
    var person = createPerson('tew', 28)
    console.log(person) // {name: "tew", age: 28}
    console.log(person.constructor) // ƒ Object() { [native code] }
-------------本文结束感谢您的阅读-------------
0%