《Javascript高级程序设计》第六章笔记整理
一、创建对象的方法(6种)
1.工厂模式
即用函数来封装以特定接口创建对象的细节。
function createPerson(name,age,job){
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
alert(this.name);
}
return o;
}
var person1 = createPerson('Lily',17,'Teacher');
var person2 = createPerson('Simon',22,'Doctor');
弊端:无法解决对象识别的问题(即怎么知道一个对象的类型)。
2.构造函数模式
function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = function(){
alert(this.name);
}
}
var person1 = new Person('Lily',17,'Teacher');
var person2 = new Person('Simon',22,'Doctor');
构造器函数始终都应该以大写字母开头;要创建新的实例,必须使用new操作符。
与工厂模式的区别:
A、没有显式地创建对象
B、直接将属性和方法赋给了this对象
C、没有return语句
创建自定义的构造函数意味着将来可以将它的实例标识为一种特定的类型,这正是构造函数胜于工厂模式的地方。
检测对象类型可使用 instanceof 操作符:
alert(person1 instanceof Object);//true
alert(person1 instanceof Person);//true
alert(person2 instanceof Object);//true
alert(person2 instanceof Person);//true
构造函数的弊端: 每个方法都要在每个实例上重新创建一遍。
在之前的例子中,person1和person2都有一个名为sayName()的方法,但两个方法不是同一个Function的实例。(因为,ECMAScript中的函数是对象,因此每定义一个函数,实际上就是实例化一个对象)从逻辑上讲,此时的构造函数也可以这样定义:
function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = new Function('alert(this.name)');//与声明函数在逻辑上是等价的。
}
以这种方式创建函数,会导致不同的作用域和标识符解析。
3.原型模式
function Person(){};
Person.prototype.name = 'Lily';
Person.prototype.age = 17;
Person.prototype.job = 'Teacher';
Person.prototype.sayName = function(){
alert(this.name);
}
var person1 = new Person();
alert(person1.sayName);//'Lily'
var person2 = new Person();
alert(person2.sayName);//'Lily'
alert(person1.sayName == person2.sayName);//true
通过 isPrptotypeOf() 方法可以确定实例与某对象原型之间的关系:
alert(Person.prototype.isPrototypeOf(person1));//true
alert(Person.prototype.isPrototypeOf(person2));//true
因为person1和person2都有一个指向Person.prototype的的指针,因而都返回了true。
虽然可以通过实例对象访问保存在原型中的值,但却不能通过对象实例重写原型中的值。如果在实例中添加了一个属性,而该属性与实例原型中的一个属性同名,那么新创建的属性就会屏蔽原型中的那个属性:
function Person(){};
Person.prototype.name = 'Lily';
Person.prototype.age = 17;
Person.prototype.job = 'Teacher';
Person.prototype.sayName = function(){
alert(this.name);
}
var person1 = new Person();
var person2 = new Person();
person1.name = 'Simon';
alert(person1.name);//'Simon' 来自实例
alert(person2.name);//'Lily' 来自原型
即,为对象实力添加一个属性时,该属性就会屏蔽原型对象中的同名属性;换句话说,添加这个属性只会阻止我们访问原型中的那个同名属性,而不会修改那个属性。即使将这个属性设置为null,也无法恢复其指向原型的链接。
想完全删除实例属性,可使用 delete 操作符:
function Person(){};
Person.prototype.name = 'Lily';
Person.prototype.age = 17;
Person.prototype.job = 'Teacher';
Person.prototype.sayName = function(){
alert(this.name);
}
var person1 = new Person();
var person2 = new Person();
person1.name = 'Simon';
alert(person1.name);//'Simon' 来自实例
alert(person2.name);//'Lily' 来自原型
delete person1.name;
alert(person1.name);//'Lily' 来自原型
使用 hasOwnProperty() 方法可以检测一个属性是存在于实例中,还是原型中(存在于实例中则返回true):
function Person(){};
Person.prototype.name = 'Lily';
Person.prototype.age = 17;
Person.prototype.job = 'Teacher';
Person.prototype.sayName = function(){
alert(this.name);
}
var person1 = new Person();
alert(person.hasOwnProperty('name'));//false
person1.name = 'Simon';
alert(person.hasOwnProperty('name'));//true
使用 in 操作符能够检测某对象是否具有某个属性(无论该属性是存在于实例中还是原型中):
function Person(){};
Person.prototype.name = 'Lily';
Person.prototype.age = 17;
Person.prototype.job = 'Teacher';
Person.prototype.sayName = function(){
alert(this.name);
}
var person1 = new Person();
alert(person.hasOwnProperty('name'));//false
alert('name' in person1); //true
person1.name = 'Simon';
alert(person.hasOwnProperty('name'));//true
alert('name' in person1); //true
故而,检测某属性是否存在于原型中可使用函数↓
function hasPrototypeProperty(object,name){
return !object.hasOwnProperty(name) && (name in object);
}
只要in操作符返回true而hasOwnProperty()返回false,即可确定属性是原型中的属性。
要取得对象上所有可枚举的实例属性,可使用ES5的 object.keys() 方法。该方法接收一个对象作为参数,返回一个包含所有可枚举属性的字符串数组:
function Person(){};
Person.prototype.name = 'Lily';
Person.prototype.age = 17;
Person.prototype.job = 'Teacher';
Person.prototype.sayName = function(){
alert(this.name);
}
var keys = Object.keys(Person.prototype);
alert(keys); //"name,age,job,sayName";
alert(Array.isArray(keys)); //true
alert(keys.length); //
var person1 = new Person();
person1.name = "Candy";
person1.job = "Singer";
var keys2 = Object.keys(person1);
alert(keys2); //"name,job";
constructor 属性不可枚举。若想获得所有实例属性(无论是否可枚举),则可使用 Object.getOwnPropertyName() 方法:
function Person(){};
Person.prototype.name = 'Lily';
Person.prototype.age = 17;
Person.prototype.job = 'Teacher';
Person.prototype.sayName = function(){
alert(this.name);
}
var keys = Object.getOwnPropertyName(Person.prototype);
alert(keys); //"constructor,name,age,job,sayName";
将 Person.prototype 设置为等于一个以对象字面量创建的新对象,是更为简单的原型语法:
function Person(){};
Person.prototype = {
name: 'Lily',
age: 17,
job: 'Teacher',
sayName: function(){
alert(this.name);
}
};
但这样的语法,本质上是完全重写了默认的 prototype 对象,使得 constructor 属性不再指向Person了(指向Object构造函数)。
可通过以下方式将constructor属性重新指向Person:
function Person(){};
Person.prototype = {
constructor: Person, //重设constructor属性
name: 'Lily',
age: 17,
job: 'Teacher',
sayName: function(){
alert(this.name);
}
};
但这种方式会导致它的[[Enumerable]](可枚举)特性被设置为true。详见高程P155.
对原型对象所做的任何修改都能立即从实例上反映出来,即便是先创建了实例后修改原型:
var friend = new Person();
Person.prototype.sayHi = function(){
alert('Hi!');
};
friend.sayHi(); //"Hi!"
但若是重写整个原型对象,情况就不一样了:
var friend = new Person();
Person.prototype = {
construcor: Person,
name: 'Candy',
age: 22,
job: 'Dancer',
sayName: function(){
alert(this.name);
}
};
friend.sayName(); //error
因为,调用构造函数时会为实例添加一个指向最初原型的指针,而把原型修改为另外一个对象就等同于切断了实例与最初原型之间的联系。
实例中的指针仅指向原型,而不指向构造函数。
重写原型对象切断了现有原型与任何之前已经存在的对象实例之间的联系,他们引用的仍然是最初的原型。
原型对象的弊端:当属性中包含引用类型值时,修改实例中相应的引用类型值将会间接修改原型中的引用类型值:
function Person(){}
Person.prototype = {
construcor: Person,
name: 'Candy',
age: 22,
job: 'Dancer',
friends: ['Mary','June'],
sayName: function(){
alert(this.name);
}
};
var person1 = new Person();
var person2 = new Person();
person1.friends.push('Van');
alert(person1.friends); //"Mary,June,Van"
alert(person2.friends); //"Mary,June,Van"
alert(person1.friends === person2.friends); //true
4.组合使用构造函数模式和原型模式
构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性:
function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.friends = ['Lily','Candy'];
}
Person.prototype = {
constructor: Person,
sayName: function(){
alert(this.name);
}
}
var person1 = new Person('Nicholas',29,'Software Engineer');
var person2 = new Person('Greg',27,'Doctor');
person1.friends.push('Simon');
alert(person1.friends); //"Lily,Candy,Simon"
alert(person2.friends); //"Lily,Candy"
alert(person1.friends === person2.friends); //false
alert(person1.sayName === person2.sayName); //true
5.动态原型模式
6.寄生构造函数模式(和工厂模式一样)
7.稳妥构造函数模式
稳妥对象:没有公共属性,其方法也不引用this的对象。
稳妥构造函数遵循与寄生构造函数类似的模式,不同之处在于:新创建的实例方法不引用this;不使用new操作符调用构造函数。
function Person(name,age,job){
//创建要返回的对象
var o = new Object();
//可以在这里定义私有变量和函数
//添加方法
o.sayName = function(){
alert(name);
};
//返回对象
return o;
}
var friend = Person('Nicholas',22,'Software Engineer');
friend.sayName(); //"Nicholas"
二、继承
1.原型链继承
基本思想:利用原型让一个引用类型继承另一个引用类型的属性和方法。
每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。故而,使原型对象等于另一个类型的实例,就实现继承:
A.prototype = new B(); //继承了B
原型链继承的实质:重写原型对象。
当以读取模式访问一个实例属性时,首先会在实例中搜索该属性,若没有找到该属性,则会继续搜索实例的原型。在通过原型链继承的情况下,搜索过程就得以沿着原型链继承向上。
所以引用类型都默认继承了Object,这个继承也是通过原型链实现的。所以函数的1默认原型都是Object的实例。P164
确定原型和实例之间的关系:使用 instanceof 和 isPrototypeOf() 方法。
alert(A instanceof B); //boolean // A是B的实例,则返回true,反之返回false
alert(A.prototype.isPrototypeOf(B)); //boolean
//A的原型也是B的原型
给原型添加方法的代码一定要放在替换原型的语句之后。
在通过原型链实现继承时,不能使用对象字面量创建原型方法,这样会重写原型方法。P166
原型链继承的弊端:问题源于包含引用类型值的原型,包含引用类型值的原型属性会被所有实例共享。
2.借用构造函数继承
即,在子类型构造函数的内部调用超类型构造函数,通过 apply() 和 call() 实现:
function Sub(){
Sup.call(this); //Sub继承了Sup
}
弊端:方法都在构造函数中定义,函数复用无从说起。
3.组合继承
将借用构造函数和原型链组合起来。
思路:通过原型链实现对原型属性和方法的继承,通过借用构造函数实现对实例属性的继承。
4.原型式继承
5.寄生式继承
6.寄生组合式继承
还是直接看书更好理解啊朋友们~
《Javascript高级程序设计》第六章笔记整理的更多相关文章
- JavaScript高级程序设计 第六章 面向对象程序设计
面向对象程序设计 ECMA-262将对象定义为:“无序属性的集合,其属性可以包含基本值.对象或者函数.”严格来讲,这就相当于说对象是一组没有特定顺序的值.对象的每个属性和方法都有一个名字,而每个名字都 ...
- JavaScript高级程序设计-第六章面向对象的程序设计
创建对象主要的两种形式,创建Object实例和创建对象字面量 对象包含属性和方法 数据 .属性有四个特性,特性是为了描述属性行为的,他们是: Configurable(可配置的)是否能删除或是否能修改 ...
- 《JavaScript高级程序设计》——第二章在HTML使用JavaScript
这章讲的是JavaScript在HTML中的使用,也就是<script>元素的属性.书中详细讲了async.defer.src和type四个<script>的属性. 下面是对第 ...
- JavaScript 高级程序设计 第5章引用类型 笔记
第五章 引用类型 一.object类型 1.创建方法: 1.使用new 操作符创建 var person=new object() Person.name=”Nicholasa” Porson.age ...
- JavaScript高级程序设计第20章JSON 笔记 (学习笔记)
第二十章 JSON 1.Json 可以表示三种类型的值: 1.简单值: 表示数值:5 表示字符串:“hello wrold”注表示字符串时必须使用双引号 2.对象: {“name”:“mi”,”ag ...
- JavaScript高级程序设计第14章表单脚本 (学习笔记)
第十四章 表单脚本 1.阻止默认表单提交 1.提交表单数据 1.使用type=submit提交按钮 2.使用submit():方法 注意:当用户点击提交按钮时,会触发submit事件,从而在这里我们有 ...
- javascript高级程序设计第三章的一些笔记
[TOC] 1. 语法 1.1 区分大小写 变量.函数名和操作费都区分大小写. 1.2 标识符 标识符指变量.函数.属性的名字,或者函数的参数.标识符按以下规则组合: 第一个字符必须是一个字母,下划线 ...
- 读书笔记 - js高级程序设计 - 第六章 面向对象的程序设计
EcmaScript有两种属性 数据属性 和 访问器属性 数据属性有4个特性 Configurable Enumerable Writable Value 前三个值的默认值都为false ...
- 读书时间《JavaScript高级程序设计》六:事件
Javascript与HTML之间的交互是通过事件实现的. 1. 事件流 事件流描述的是从页面中接收事件的顺序. <!DOCTYPE html> <html> <head ...
随机推荐
- ElasticSearch6.5.0【Java客户端之TransportClient】
说明 TransportClient:网上流传最多的客户端,目前最新版本 Java REST Client:官方推荐的客户端, 官方:我们要在Elasticsearch 7.0的版本中不赞成使用Tra ...
- python学习笔记—Day1
1. python使用<变量名>=<表达式>的方式对变量进行赋值 a=1; python中数分为整数和浮点数 字符串的定义一定要用引号,单引号和双引号是等价的 三引号用来输入包 ...
- Codeforces Round #529 (Div. 3) F.Make It Connected
传送门 题意: 有 n 个顶点,每个顶点有个花费 a[ i ],连接顶点 u,v 需要花费 a[v]+a[u]的代价. 有 m 个特殊边,每条边有三个参数 u,v,w 代表的意思是连接 u,v 的花费 ...
- Jquery Mobile表单
三个前提: 1.每个form必须设置method和action属性 2.每个form必须有页面范围内唯一的id标识 3.每个form必须有一个label标签,通过设置它的for属性来匹配元素的id & ...
- 【清北学堂2018-刷题冲刺】Contest 5
这三个题写了一天半,第一个题写了大概一整天.出题人劝我从后往前写,我不听,结果T1想了+调了一天QWQWQ Task 1:序列 [问题描述] 定义一个"好的序列"为一个长度为M ...
- BZOJ2157 边转点 树链剖分
https://www.lydsy.com/JudgeOnline/problem.php?id=2157 现在就是后悔,非常后悔 本来想随便拿个树剖热身,不料开了个毒瘤题. 题意:动态维护一棵树上的 ...
- 将本地项目上传至github
1.新建仓库:用于存放要上传的项目(尽量不要添加README). 2.找到要上传的文件夹A,右键点击git bash here,打开git界面 3.在命令行中,输入“git init”,使文件夹A加入 ...
- Linux_问题
1.远程连接项目服务器(miit_shi),遇到的问题 问题:Connecting to IP地址:22... Connection established. To escape to local s ...
- jms和activemq
一.什么是JMS JMS是java message service的缩写即java消息服务,是java定义的消息中间件(MOM)的技术规范(类似玉JDBC).用于程序之间的异步通信,如果两个应用程序需 ...
- Django之组件--auth组件
目录 Auth模块是什么 auth模块常用方法 扩展默认的auth_user表 1 Django自带的用户认证模块,可以快速的实现登录,注销,修改密码... 2 扩展auth表,需要继承Abstrac ...