重学《JavaScript 高级程序设计》笔记 第6章对象

第6章 面向对象的程序设计

ECMAScript中没有类的概念;

1.创建对象-历史

1.1 创建实例,添加方法和属性 → 对象字面量

缺点: 使用同一接口创建很多对象,产生大量重复代码

var person = new Object()
person.name = "Y"
person.age = 18
person.job = "police"person.sayName = function() {alert(this.name)
}

var person = {name = "Y",age = 18,job = "police",sayName = function() {alert(this.name)}    
}

1.2 工厂模式 (返回对象)

缺点: 没有解决对象识别的问题:怎样知道一个对象的类型;
特点: 内部创建对象,并返回

function createPerson(name, age, job) {var o = new Object()o.name = nameo.age = ageo.job = jobo.sayName = function () { alert(this.name)}return o
}

1.3 构造函数模式

缺点: 每个方法都要在每个实例上重新创建一遍!不同实例上的同名函数是不相等的,然而,创建2个完成同样任务的Function实例的确没有必要。
特点
没有显示创建对象;
没有return;
将方法属性赋值给this对象;
首字母大写;
构造函数创建的实例标识为一种特定的类型

1.3.1 理解面试题

调用构造函数创建对象,实际会经历以下4个步骤:

  1. 创建一个新对象;
  2. 将构造函数的作用域赋值给新对象(this指向这个新对象)
  3. 执行构造函数代码(为这个新对象添加属性方法)
  4. 返回新对象
function Person(name, age, job) {this.name = namethis.age = agethis.job = jobthis.sayName = function () { // 每定义一个函数,就是实例化一个对象alert(this.name)}
}
var p1 = new Person("Y",18,"police")
var p2 = new Person("H",8,"teacher")
// p1.constructor == Person
// p2.constructor == Person// ( p1 instanceOf Object  )  true
//  ( p1 instanceOf Person  )  true
// ( p2 instanceOf Object  )  true
//  ( p2 instanceOf Person  )  true
  • 在全局作用域调用函数,this总是指向GLOBAL对象
function person(name, age, job) {this.name = namethis.age = agethis.job = jobthis.sayName = function () { alert(this.name)}
}
person('Leo', 16, 'doctor') 
window.sayName() //leo

1.3.2 将构造函数当做函数

任何函数,只要通过new操作符来调用,便可以作为构造函数;若不用,即与普通函数无异。this会指向Global对象(在浏览器中就是window)

1.3.3 将方法定义到构造函数外部

缺点: 在全局作用域上定义的函数,若只能被某个对象调用,不合理;并且,如果对象需要定义很多方法,则需要定义很多个全局函数,对于这个自定义的引用类型就丝毫没有封装性可言。

function Person(name, age, job) {this.name = namethis.age = agethis.job = job
}
function sayName() {alert(this.name)
}

1.4 原型模式

1.4.1 理解原型模式

function Person() {}
Person.prototype.name = "Y" 
Person.prototype.age = 18
Person.prototype.job = "police" Person.prototype.sayName = function () {alert(this.name)
}
  1. 使用原型对象的好处:让所有的对象实例共享它所包含的属性和方法;
  2. 只要创建了一个新函数,就会为该函数创建一个prototype属性,指向函数的原型对象
  3. 原型对象会自动获得constructor属性,该属性包含一个指向prototype属性所在函数的指针(constructor属性指向构造函数);
  4. 对象的constructor属性最初是用来表示对象类型的;
  5. 构造函数创建的实例,实例内部包含一个指针,指向构造函数的原型对象
  6. Person.prototype.isPrototypeOf(p1) // true
  7. Object.getPrototypeOf(p1) == Person.prototype // true
  8. 代码读取某个对象某个属性时,先搜索对象实例,若无再搜索原型对象;
  9. 若实例中添加的属性和原型属性同名,会屏蔽原型中的属性(因为实例只能访问原型中的值,而不能重写);
  10. 若将实例中的同名属性设为null,并不能恢复与原型的连接,需要使用delete操作符完全删除实例属性
  11. p1.hasOwnProperty("name")// true hasOwnProperty()方法可以检测一个属性是存在于实例中还是存在于原型,若来自实例则返回true
    在这里插入图片描述

1.4.2 in操作符

in操作符会在通过对象能访问给定属性时返回true,无论属性存在于实例还是原型中

"name" in p1 // true

1.4.3 确认属性存在于原型

function hasPrototypeProperty(object,name) {return !object.hasOwnProperty(name) && (name in object)
}

1.4.4 更简单的原型语法

缺点: constructor属性不再指向Person,切断了实例和原型对象的联系;对包含引用类型值的属性,被实例共享会造成问题。
特点: 减少不必要的输入(每添加一个属性/方法就要多敲一遍Person.prototype)

将Person.prototype设置为一个以对象字面量形式创建的新对象,本质上重写了prototype对象(创建函数时自动创建的原型对象),导致constructor属性指向Object构造函数,尽管instanceof还能返回正确的结果。

function Person() {}
Person.prototype = {name : "Y",age : 18,job : "police",sayName: function () {alert(this.name)}
}
p1 instanceof Object // true
p1 instanceof Person // true
p1.constructor == Person // false
p1.constructor == Object // true

增加constructor属性,确保该属性能访问到适当的值。

function Person() {}
Person.prototype = {constructor : Person,name : "Y",age : 18,job : "police",sayName: function () {alert(this.name)}
}

1.4.5 原生对象的问题

function Person() {}
Person.prototype = {constructor : Person,friends:["Yoona","Jessica"], // 数组,引用类型name : "Y",age : 18,job : "police",sayName: function () {alert(this.name)}
}
p1.friends.push("Krystal")
p1.friends == p2.friends

1.4.6 组合使用构造函数模式和原型模式

构造函数模式用于定义实例属性,原型模式用于定义方法和共享的属性。(实例属性中的引用类型互不干扰)

function Person(name, age, job) {this.name = namethis.age = agethis.job = jobthis.friends = ["Yoona","Jessica"]
}Person.prototype = {constructor : Person,sayName: function () {alert(this.name)}
}

1.4.7 原生对象的原型

通过原生对象的原型,不仅可以取得所有默认方法的引用,也可以定义新方法。但不推荐在产品化的程序中修改原生对象的原型:
如果因为某个实现中缺少某个方法,就在原始对象的原型中添加,那么当在另一个支持该方法的实现中运行代码时,就可能会导致命名冲突,而且这样做也可能会意外地重写原生方法。

1.5 梳理

在这里插入图片描述

1.6 修改原生对象的原型

String.prototype.startsWith = function(){}

在当前环境中,所有字符串都可以调用startsWith,但不推荐,可能会产生命名冲突,也可能会意外地重写原生方法。

String.prototype.
toString = function () {console.log('修改原生对象默认方法')
}'str'.toString() // 修改原生对象默认方法

将原生对象原型指向空,没效果

String.prototype = null
console.log('str'.toString()) // str

2. 属性类型 P139

2.1 数据属性

  1. [[Configurable]]:能否通过delete删除属性,默认为true
  2. [[Enumerable]]:能否通过for-in循环返回属性,默认为true
  3. [[Writeable]]:能否修改属性的值,默认为true
  4. [[Value]]:属性值,默认为undefined
  • 一旦把属性定义为不可配置的,就不能再把它变回可配置了;
  • 要修改属性默认的特性,必须使用Object.defineProperty(),3个参数:对象、属性名、描述符对象(属性必须是数据属性),若不指定数据属性,则默认为false;

2.2 访问器属性

  1. 包含一对getter和setter函数(都不是必须的),有4个特性:
    1) [[Configurable]]
    2) [[Enum而able]]
    3) [[Get]]:读取属性时默认调用的函数,默认值undefined
    4) [[Set]]:写入属性时默认调用的函数,默认值undefined
  • 属性 _year 前面的下划线用于表示只能通过对象方法访问的属性;
  • 使用访问器属性的常见方式:设置一个属性的值会导致其他属性发生变化;

2.3 读取属性

Object.getOwnPropertyDescriptor(),2个参数:对象、属性

var descriptor = Object.getOwnPropertyDescriptor(person,"age")
alert(age.value)
alert(age.enumerable)
alert(age.configurable)
alert(age.get)

方法集合

Object.defineProperty() // 修改属性默认值
Object.getOwnPropertyDescriptor() //读取属性的描述符
谁谁的原型对象.isPrototypeOf(实例) // 参数是实例,判断实例内部是否有指向构造函数原型对象的指针
hasOwnProperty() // 检测属性来自实例(返回true)还是原型

3. 继承(子类继承父类的特征和行为)

3.1 原型链

原型链的问题

  1. P166 原型链虽然很强大,可以用它来实现继承,但它也存在一些问题,其中最主要的问题来自包含引用类型值的原型。包含引用类型值的原型属性会被所有实例共享,因此要在构造函数,而不是原型对象中定义属性。
  2. 在创建子类型的实例时,不能向超类型的构造函数中传递参数。(没有办法在不影响所有对象实例的情况下,给超类型的构造函数传递参数)

让原型对象等于另一个类型的实例。

SubType.prototype = new SuperType()

SubType的实例指向SubType的原型,进而又指向SuperType的原型
在这里插入图片描述

3.2 默认的原型

所有引用类型默认都继承了Object,所有函数的默认原型都是Object的实例,因此默认原型都会包含一个内部指针,指向Object.prototype,这也正是所有自定义类型都会继承toString()、valueOf()等默认方法的根本原因。完整的原型链如下:
在这里插入图片描述

instance instanceof Object // true
instance instanceof SuperType // true
instance instanceof SubType // trueObject.prototype.isPrototypeOf(instance) // true
SuperType.prototype.isPrototypeOf(instance) // true
SubType.prototype.isPrototypeOf(instance) // true
function Fun() {}console.log(Fun.prototype.__proto__==Object.prototype) // true

3.3 组合继承

  1. 父构造函数内有引用类型属性值
  2. 子构造函数内使用call(保证了实例不会都指向同一个引用类型)
  3. 子构造函数原型指向父构造函数原型(Object.create
  4. 子构造函数原型里的构造器指向自己(知晓实例由谁创建)
    在这里插入图片描述

4. 方法整理

  • A.isPrototypeOf(a)
    // 实例a的__proto__指向A的原型
    // 判断原型对象A是否是实例a的原型,是则返回true
  • a.hasOwnProperty(‘name’) 判断属性是否存在于实例中(实例属性),是则返回true
    Object.keys() 获得对象上所有可枚举的属性
  • ‘name’ in a 无论原型/实例,只要是能访问得到的属性,in操作符返回true(包括constructor)'name’字符串
    for-in循环,返回所有能通过对象访问的、可枚举的属性 ,(遍历一个实例对象,原型上的属性也会打印、要只打印实例属性,需要配合hasOwnProperty
  • Object.getOwnPropertyNames(),得到所有实例属性,包括constructor
function Test(name) {this.name = name
}
Test.prototype.name = 'hhh'
let tObj = new Test('yyy')
console.log('name' in tObj) // true
console.log('constructor' in tObj) // true
for(var prop in tObj){console.log('for-in', prop) // name
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/251344.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java-reflect(反射)初步理解_1

27.01_反射(类的加载概述和加载时机) A:类的加载概述 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。加载 就是指将class文件读入内存,并为之创建一个…

javascrip --- 构造函数的继承

两点需要注意的. 第一是在构造函数声明时,会同时创建一个该构造函数的原型对象,而该原型对象是继承自Object的原型对象 // 声明一个构造函数Rectengle function Rectangle(length, width) {this.length length;this.width width; }// 即:看见function 后面函数名是大写,一般…

Ruby实例方法和类方法的简写

创建: 2017/12/12 类方法 Sample.func实例方法 Sample#func转载于:https://www.cnblogs.com/lancgg/p/8281677.html

《JavaScript 高级程序设计》笔记 第7章及以后

第7章 函数表达式 匿名函数的name属性是空字符串;闭包是函数:闭包是有权访问另一个函数作用域中变量的函数;(P181 副作用,解释了点击li弹出循环最后值的原因)当某个函数第一次被调用时,会创建一个执行环境及相应作用域链&#xf…

[树形dp] Jzoj P1046 寻宝之旅

Description 探险队长凯因意外的弄到了一份黑暗森林的藏宝图,于是,探险队一行人便踏上了寻宝之旅,去寻找传说中的宝藏。藏宝点分布在黑暗森林的各处,每个点有一个值,表示藏宝的价值。它们之间由一些小路相连&#xff0…

javascript --- 使用语法糖class定义函数

本文讨论的是通过class声明的函数,有什么特点,或者说是指向了哪里. class A() {} // A是一个类// 要看class声明的函数指向哪里,只需将其[[Prototype]]属性打印到控制台,下面看看A和它的原型对象的指向 // 注:[[Prototype]]属性通过__proto__访问 console.log(A.__proto__…

前端知识点整理收集(不定时更新~)二

目录 require() 加载文件机制 线程和进程 线程 单线程 Nodejs的线程与进程 网络模型 初识 TCP 协议 三次握手 I/O I/O 先修知识 阻塞与非阻塞 I/O 同步与异步 I/O Git 基础命令 分支操作 修改远程仓库地址 远程分支获取最新的版本到本地 拉取远程仓库指定分支…

SpringBoot零基础入门指南--搭建Springboot然后能够在浏览器返回数据

File->new Project 修改默认包名,根据自己的喜好修改 选择初始化需要导入的包,尽量不要一开始就导入很多,特别是数据库,不然启动可能会有问题,创建好的目录如下: 配置文件写在application.properties下&…

JavaScript算法相关

1. 排序 1.1.冒泡排序 每一轮比较&#xff0c;从左至右交换相邻&#xff0c;每轮结束&#xff0c;最后一个为最大下一轮&#xff0c;需要比较的个数 - 1 j < len - i (范围动态缩小)共 len - 1 轮比较 function bubbleSort(arr) {var len arr.length;for (var i 1; i &…

javascript --- 编程风格

字符串 const a foobar; const b foo${a}bar; // 此处是反引号(tab键上) const c foobar;解构赋值 const [first, second] arr;function getFullName({ firstName, lastName }) { }function processInput(input) {return { left, right, top, bottom }; } const { left…

$ - 字符串内插

$ 特殊字符将字符串文本标识为内插字符串。 内插字符串是可能包含内插表达式的字符串文本。 将内插字符串解析为结果字符串时&#xff0c;带有内插表达式的项会替换为表达式结果的字符串表示形式。 此功能在 C# 6 及该语言的更高版本中可用。 与使用字符串复合格式设置功能创建…

数据结构基础知识

排序 参考&#xff1a;https://www.bilibili.com/video/av38482633/?spm_id_fromtrigger_reload 目录 排序 插入排序 直接插入排序 折半排序 希尔排序 ​ 交换排序 冒泡排序 快速排序 选择排序 堆排序 流量单位计算 什么是计数排序 复杂度分析&#xff1a; 什…

linux中安装软件,查看、卸载已安装软件方法

各种主流Linux发行版都采用了某种形式的包管理系统&#xff08;PMS&#xff09;来控制软件和库的安装。 软件包存储在服务器上&#xff0c;可以利用本地Linux系统上的PMS工具通过互联网访问。这些服务器称为仓库。 由于Linux发行版众多,目前还没有统一的PMS标准工具。 这里分别…

html5 --- 使用javascript脚本控制媒体播放

H5中的标签(<audio…/> 和 <video…/>)对于JS中的HTMLAudioElement对象和HTMLVideoElement对象 对象有以下几个方法: play(): 播放 pause(): 暂停播放 load(): 重新装载音频、视频 canPlayType(type): 判断该元素可播放type类型的音频、视频 下面是一个简单的音乐…

在js中if条件为null/undefined/0/NaN/表达式时,统统被解释为false,此外均为true

Boolean 表达式 一个值为 true 或者 false 的表达式。如果需要&#xff0c;非 Boolean 表达式也可以被转换为 Boolean 值&#xff0c;但是要遵循下列规则&#xff1a; 所有的对象都被当作 true。当且仅当字符串为空时&#xff0c;该字符串被当作 false。null 和 undefined 被当…

ES6专题——整理自阮一峰老师的ECMAScript 6入门

这里我仅仅是记录了那些我认为值得注意的ES6知识点&#xff0c;详细版请挪步https://es6.ruanyifeng.com/#docs/let let和const命令 let声明的变量只在它所在的代码块有效。 var a []; for (let i 0; i < 10; i) {a[i] function () {console.log(i);}; } a[6](); // 6 …

开发测试比

1.服务器已经开启了CORS跨域支持 浏览器有同源策略限制&#xff1a;协议、域名、端口号其中无法向非同源地址发送ajax请求 跨域解决方法&#xff1a;JSONP&#xff08;只支持get不支持post&#xff09;&#xff0c;不是ajax 凡是有src属性的标签都有跨域能力 前端定义一个处理…

map函数用法详解

map函数是Python内置的高阶函数&#xff0c;它是一个典型的函数式编程例子。它的参数为: 一个函数function、一个或多个sequence。通过把函数function依次作用在sequence的每个元素上&#xff0c;得到一个新的sequence并返回。注意&#xff1a;map函数不改变原有的sequence&…

2018暑假集训测试六总结

拿到试题没几分钟&#xff0c;就有人说会做T1QAQ。第一题感觉似曾相识&#xff0c;其实不同。梳理出本质后发现有两个限制&#xff0c;便想用枚举递推来快速求解&#xff0c;发现要么是不会推&#xff0c;要么是时空超限&#xff0c;不会优化。期间也想过通过离线做&#xff0c…

css3 --- 使用媒体查询进行响应式布局

css3引入media,可以根据设备特性进行不同的布局, 本文展示的是根据不同屏幕的宽度进行不同的布局,代码如下: <!DOCTYPE html> <html> <head><meta http-equiv"Content-Type" content"text/html; charsetutf-8" /><title> 针…