对象
1 对象类型定义
2 对象实例
3 对象表示方式(对象字面量)
4 对象属性和方法,及属性特性
5. 原型与原型链
6. 面向对象设计编程
1 对象类型定义:无序属性的集合,其属性可以包含简单数据类型值,函数或者对象。
2 对象实例:(对象类型的值) 每种数据类型都有相应的值,比如数字8是Number类型的一个值。对于对象类型,值被称为对象实例,任意一种对象都是object对象类型的实例(值)。比如简单类型包装对象(boolean,string,number)就是对象类型的值(实例)。
3 对象的表示方式:(对象字面量) 最外层的一对大括号({})就表示这是一个对象字面量。数组类型的实例也可以直接通过数组字面量来表示。
//对象字面量和数组字面量 //流行的JSON数据格式就是基于如此,不过两者有区别, { //对象字面量 name: 'Games', getName:fucntion(){ return this.name; } } [{ //数组字面量 name: 'Games', age:'31' },{ name: 'Kobe', age:'34' } ]
3.1 对象字面量直接创建对象
var person = { "name":"Games", "age":"31" }; //用对象字面量创建对象,实际没有利用构造函数 //但创建出的对象仍然有指向这个函数的属性 //constructor用于保存创建这个对象实例的函数 console.info(person.constructor===Object);//true
4. 对象属性和方法,及属性特性
方法本质也是一种属性,只是属性的值类型是函数,就称为方法。
1_属性类型:
数据属性:直接属性值的属性
访问器属性:通过getter/setter方法来访问属性值的属性
内部属性:不能通过代码直接访问的属性,只是为了规范说明目的而存在
2_常见内部属性 (在规范中也使用两个中括号的形式来描述)
哪些对象具备 | 内部属性名称 | 说明 |
所有对象 | [[Put]] | 属性赋值调用的方法 |
所有对象 | [[Get]] | 读取一个属性值调用的方法 |
所有对象 | [[Prototype]] | 指向每一个对象的原型对象,不能在代码中直接访问这个内部属性, 可以通过Object.getPrototypeOf(object)来获取原型对象(在Firefox中可以通过__proto__来直接访问)。 |
所有对象 | [[Class]] | Object.prototype.toString方法中按照规范内建对象会返回包含[[Class]]的值“[object class]”, 而内建对象的[[Class]]值就是相应的名称(比如Array对象的[[Class]]值为'Array'), 因此可以通过Object.prototype.toString.call(value) == '[object Array]'来判断value是否是一个Array。 |
New创建对象 | [[Construct]] | 使用new操作符调用一个函数时,后台调用[[Construct]] |
调用call方法 | [[Call]] | 而使用call方法来调用函数时,后台会调用[[Call]] |
执行函数时 | [[Scope]] |
当一个函数被执行时,就会创建一个[[Scope]]对象,可以理解为[[Scope]]就是函数活动对象,而this、arguments、形参、函数内部定义的变量和函数都是的[[Scope]]对象的属性。 |
3_属性定义及定义属性相关特性(用来描述属性的特性)
最常见的定义属性的方法就是直接在对象上添加属性,比如obj.name = 'linjisong',这种情况下定义的属性所具有的内部特性都是默认的。
如果想定义一个值不能被修改的属性要怎么做呢?
属性定义方法:defineProperty() 定义一个属性 和 defineProperties() 定义一组属性
属特相关特性:
特性含义 | 特性名称 | 直接添加属性时默认值 | 通过方法定义属性时默认值 |
属性具体的值 | value | undefined | undefined |
能够修改属性值 |
writable |
true 可以 |
false 不可以 |
能否通过for-in枚举返回属性 | enumerable | true 可以 | false 不可以 |
能够重新配置属性特性或删除属性 | configurable | true 可以 | false 不可以 |
读取属性时调用的函数 | get | undefined | undefined |
写入属性时调用的函数 | set | undefined | undefined |
//注意说明 // 1. 对于访问器属性,[[Get]]、[[Set]]不一定都有,没有[[Get]]的属性不能读(返回undefined,严格模式下抛出异常),没有[[Set]]的属性不能写(会忽略,严格模式下抛出异常) // 2. 使用属性定义方法时 [[Enumerable]] [[Configurable]] [[Writable]] 默认值为false。 // 3. 这些方法设置或获取的属性特殊和属性的类型有关,比如数据属性只能设置[[Confirurable]]、[[Enumerable]]、[[Writable]]、[[Value]]。
定义属性:
// 创建一个包含一个默认属性job的对象(job属性可以修改、删除、在for-in中枚举) var person = {job:'it'}; Object.defineProperty(person, 'name', { // 添加一个不能被修改、删除的name属性 value:'Games', enumerable:true //可以通过for-in遍历,但是Configurable,Writable都为false.所以不能被修改和删除 }); Object.defineProperties(person, { // 定义多个属性(数据属性year和访问器属性age) year:{ //数据属性year可以直接被访问 value : 2012, configurable:true, writable:true }, age:{ //访问器属性age get : function(){ return this.year-1984; } } }); var job = Object.getOwnPropertyDescriptor(person, 'job'); console.info(job.configurable); //true,直接添加属性时默认为true var name = Object.getOwnPropertyDescriptor(person, 'name'); console.info(name.configurable); //false,使用属性定义方法添加属性时默认为false console.info(person.name); //Games
person.name = 'Kobe'; //由于不能修改,所以值不会改变,在严格模式下会抛出异常 console.info(person.name); //Games person.year = 2015; console.info(person.year); //2015 console.info(person.age); //31,在修改year的同时,也修改了age属性
对象常用属性和方法:
类型 |
名称 |
说明 |
属性 |
constructor |
指向用于创建当前对象的构造函数 |
方法 |
hasOwnProperty(propertyName) |
检查给定的属性是否在当前对象实例中 |
propertyIsEnumerable(propertyName) |
检查给定的属性是否能够是使用for-in语句来枚举 | |
isPrototype(object) | 检查传入的对象是否是另一个对象的原型 | |
toLocalString() | 返回对象的字符串表示,该字符串与执行环境的地区相对应 | |
toString() | 返回对象的字符串表示 | |
valueOf() | 返回对象的字符串、数值或布尔值表示,通常与toString()方法返回值相同 | |
create(prototype[,descriptors]) |
创建一个具有指定原型且可选择性地包含指定属性的对象 | |
getOwnPropertyNames(object) |
返回对象的属性(方法)的名称 | |
getPrototypeOf(object) |
返回对象的原型 | |
keys(object) |
返回对象的可枚举属性(方法)的名称 |
对象属性和方法访问方式:
确定值:用.点号方式访问,如:person1.name;
不确定值:使用方括号[],如: oDiv[this.index]; 方括号内部可以是一个变量或者表达式。
如何遍历对象实例自身的属性而不包括从原型链继承而来的属性。
for(var propertyName in object){ //如果for-in循环 if(object.hasOwnPorperty(propertyName)){ //通过方法判断是否属于自身对象的属性 //循环处理 } }
5. 原型与原型链
1. 原型与原型链
每个对象都有一个原型对象,而原型对象本身也是一个对象,也有自己的原型对象,这样就形成了一个圆形链直至null对象。
对象的内部属性[[Prototype]]指向的就是对象的原型对象,而Object.prototype的原型对象为null。
2. 原型链作用-属性方法查找-继承
在访问一个对象的属性(方法)时,JS引擎会先查找对象本身有没有这个属性,如果有,返回这个属性值,如果没有,则查找对象的原型是否有这个属性,有则返回,如果没有就继续查找原型对象的原型直至最后一个原型对象。
3. 对象的原型(__proto__), 构造函数的原型(prototype)
每一个构造函数都有一个属性prototype,这个属性是在函数定义过程中添加的,它指向的对象就是所有使用该函数创建的实例对象的原型对象。
构造函数的原型对象有一个属性constructor指向该构造函数。
1.所有函数(构造器)的__proto__都指向Function.prototype.
2.所有对象的原型(__proto__)都指向其构造器的prototype.
4. 原型链与作用域链区别
属性查找是沿着原型链,标识符查找是沿着作用域链,都是一个逐级查找的过程。
6. 面向对象设计编程
1. JS中没有类(class),但是利用构造函数及其原型对象来替代,
2. 类和对象的区别于关系
类是抽象的,代表一类事物;对象是具体的,代表一个实际的事物;类是对象实例的模板,对象实例是类的一个个体。
3. 抽象
在定义一个类时,实际上就是把一类事物的共有属性和行为提取出来,形成一个模板。
4. 面向对象三大特征
封装:封装就是把抽象出来的属性和对属性的操作方法封装在一起,属性被保护在内部,程序的其它部分只有通过被授权的操作(函数),才能对属性进行操作,比如公开的登录方法,验证通过则可以查看私有的属性,比如账户余额等!
继承:实例化对象可以继承其对应类的属性和方法,类也可以继承其它类的属性和方法,代码复用,提高效率
多态:即多种状态,就是指一个引用在不同情况下的多种状态,通过指向父类的引用,来调用不同子类中实现的方法。JS中一个变量的类型是在运行过程中由JS引擎决定的。JS不支持重载,子类可以覆盖父类的方法,达到不同状态实现不同的功能需求。
定义类及实例化对象方法: 用构造函数方式来定义对象特有属性,用原型方式来定义类共有的属性和方法。
子类继承父类的方法: 用构造函数方式实现对父类属性的继承,用原型方式实现对原型属性和方法的继承。
//定义一个父类 function Person(name,sex){ //用构造函数方式来定义对象特有属性 this.name = name; this.sex = sex; } Person.prototype.sayHello = function(){ //用原型方式来定义类共有的属性和方法。 return "Hello!"; }; //定义一个子类 function Student(name,sex,major){ //用构造函数方式来继承父类的属性 Person.call(this,name,sex,major); //创建子类实例时,第二次调用父类构造函数,并利用call改变this指向 this.major = major; } Student.prototype = new Person(); //通过原型继承方式,子类的原型对象指向父类的实例,可以继承父类原型方法 Student.prototype.study = function(){ //子类的方法 return "Never too late to learn!"; } var s1 = new Student('Games','male','IT'); //实例化子类的对象,并初始化 console.info(s1.name); //Games 继承父类的属性 console.info(s1.sex); //male 继承父类的属性 console.info(s1.sayHello()); //Hello 继承父类的方法 console.info(s1.major); //IT 子类自身的属性 console.info(s1.study()); //Never too late to learn! 子类自身的方法
寄生组合式继承:这种方式只调用了一次父类构造函数,从而避免了在子类型的原型对象上创建不必要的属性,也能够保证原型链不变,可以正常使用instanceof和isPrototypeOf()。function create(o){ //创建一个新对象并返回 var fn = function(){}; fn.prototype = o; return new fn(); } function inherit(sub, sup){ //实现子类原型继承父类原型实例 var prototype = create(sup.prototype); prototype.constructor = sub; sub.prototype = prototype; } //定义一个父类 function Person(name,sex){ //用构造函数方式来定义对象特有属性 this.name = name; this.sex = sex; } Person.prototype.sayHello = function(){ //用原型方式来定义类共有的属性和方法。 return "Hello!"; }; //定义一个子类 function Student(name,sex,major){ //用构造函数方式来继承父类的属性 Person.call(this,name,sex,major); //创建子类实例时,第二次调用父类构造函数,并利用call改变this指向 this.major = major; } inherit (Student,Person); //通过inherit方法继承 Student.prototype.study = function(){ //子类的方法 return "Never too late to learn!"; } var s1 = new Student('Games','male','IT'); //实例化子类的对象,并初始化 console.info(s1.name); //Games 继承父类的属性 console.info(s1.sex); //male 继承父类的属性 console.info(s1.sayHello()); //Hello 继承父类的方法 console.info(s1.major); //IT 子类自身的属性 console.info(s1.study()); //Never too late to learn! 子类自身的方法