ALEX CODE PARK

vuePress-theme-reco ALEX CODE PARK    2020
ALEX CODE PARK

Choose mode

  • dark
  • auto
  • light
阅读笔记
  • 《深入浅出Vue.js》
  • 《JavaScript高级程序设计》
  • 《你不知道的JavaScript》
  • 《CSS世界》
前端面试
  • CSS
  • HTML
  • JavaScript
  • 常见算法
  • 网络相关
  • 剑指Offer
Github
Tag
Category
  • 前端面试
  • 算法题目
  • 前端书籍
  • 个人笔记
author-avatar

ALEX CODE PARK

19

文章

10

标签

阅读笔记
  • 《深入浅出Vue.js》
  • 《JavaScript高级程序设计》
  • 《你不知道的JavaScript》
  • 《CSS世界》
前端面试
  • CSS
  • HTML
  • JavaScript
  • 常见算法
  • 网络相关
  • 剑指Offer
Github
Tag
Category
  • 前端面试
  • 算法题目
  • 前端书籍
  • 个人笔记
  • JavaScript高级程序设计

    • 第六章 面向对象的程序设计
      • 6.1 理解对象
      • 6.2 创建对象
      • 6.3 继承
    • 第十三章 事件
      • 13.1 事件流
      • 13.2 事件处理程序
      • 13.3 事件对象
      • 13.4 事件类型
      • 13.5 内存和性能

JavaScript高级程序设计

vuePress-theme-reco ALEX CODE PARK    2020

JavaScript高级程序设计


AlexChiang 2020-02-15 JavaScript

# 第六章 面向对象的程序设计

# 6.1 理解对象

# 6.1.1 属性类型

ECMAScript 有两种属性:

  • 数据属性:

    • Configurable:能否使用 delete 删除属性
    • Enumerable:能否使用 for-in
    • Writable:能否修改属性值
    • Value:包含属性的数据值
  • 访问器属性:

    • Configurable:同上
    • Enumerable:同上
    • Get
    • Set

    一般使用 object.__defineGetter__('attributeName',funcion())

# 6.1.2 定义多个属性

var book = {}

Object.defineProperties(book, {
	a:{
		writable:true,
		value:1
	},
	b:{...}
})

# 6.1.3 读取属性

Object.getOwnPropertyDescriptor()

返回值是一个对象

# 6.2 创建对象

# 6.2.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("Nicholas", 29, "Software Engineer");
  var person2 = createPerson("Greg", 27, "Doctor");

  person1.sayName();   //"Nicholas"
  person2.sayName();   //"Greg"

# 6.2.2 构造函数模式

如 Object 和 Array 就是原生的构造函数

工厂模式解决了创建多个相似对象的问题**(太单一)**却没有解决 对象识别的问题

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

var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor");

person1.sayName();   //"Nicholas"
person2.sayName();   //"Greg"
# 与工厂模式不同之处:
  • 没有显示创建对象

  • 直接将属性方法赋值给 this

  • 没有 return

  • 使用 new 创建并经历以下四个步骤:

    1.创建一个新对象

    2.将构造函数作用域赋给新对象 (this 指向新对象)

    3.执行构造函数中的代码

    4.返回新对象

# 检测对象类型:
  • 创建的对象既是 Object 的实例也是 Person 的实例

  • person1 person2 分别保存着 Person 的一个不同的实例,这两个对象都有一个 constructor 属性,该属性指向 Person (构造函数特有?)

    alert(person1 instanceof Object); //true alert(person1 instanceof Person); //true alert(person2 instanceof Object); //true alert(person2 instanceof Person); //true

    alert(person1.constructor == Person); //true alert(person2.constructor == Person); //true

    alert(person1.sayName == person2.sayName); //false

# 将构造函数当做函数:

任何函数只要通过 new 来调用就是构造函数 与普通函数的唯一区别就是调用方式

// 构造函数
var person = new Person("Nicholas", 29, "Software Engineer");
person.sayName();   //"Nicholas"

// 普通函数
Person("Greg", 27, "Doctor");  //adds to window
window.sayName();   //"Greg"

// 在另一对象的作用域中调用
var o = new Object();
Person.call(o, "Kristen", 25, "Nurse");
o.sayName();    //"Kristen"

# 6.2.3 原型模式

构造函数模式的问题:导致不同的作用域链和标识符解析

我们创建的每个函数都有一个 prototype 属性,这个属性是一个指针,指向原型对象

# 原型模式创建对象:
function Person(){
}

Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){
    alert(this.name);
};

var person1 = new Person();
person1.sayName();   //"Nicholas"

var person2 = new Person();
person2.sayName();   //"Nicholas"

alert(person1.sayName == person2.sayName);  //true

读取对象的某个属性时,都会执行一次搜索。先从实例本身开始再到原型对象。

# 原型链:

Person.prototype → 原型对象

Person.prototype.constructor → Person

person1.[[prototype]] = person1.proto → 原型对象

person1 与 Person 没有直接联系

# 检测:
alert(Person.prototype.isPrototypeOf(person1));  //true
alert(Person.prototype.isPrototypeOf(person2));  //true

//only works if Object.getPrototypeOf() is available
if (Object.getPrototypeOf){
    alert(Object.getPrototypeOf(person1) == Person.prototype);  //true
    alert(Object.getPrototypeOf(person1).name);  //"Nicholas"
}
  • 使用hasOwnProperty()方法可以检测一个属性是否存在于实例中

  • 使用in操作符只要该属性能够被访问即返回 true e.g: 'name' in person1

  • 要取得对象上所有可枚举的 实例 属性可以使用Object.keys()

    var keys = Object.keys(Person.prototype); alert(keys); //"name,age,job,sayName"

  • 要取得对象上所有 实例 属性无论可否枚举使用Object.getOwnPropertyNames()

# 更简单的原型语法:

省去每次都要敲一遍 Person.prototype 的麻烦

// 原本的原型语法
function Person(){
}

Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){
    alert(this.name);
};

// 更简单的原型语法
function Person(){
}

Person.prototype = {
	// consructor: Person, 如不设置则constructor不再指向Person而是指向Object
	name:'Nicolas',
	age:29
};
# 原型的动态性:

重写原型对象会切断实例与原有原型的联系

/*
重写前
friend.[[prototype]] -> Person Prototype
Person.prototype -> Person Prototype
Person Prototype.constructor -> Person
*/
function Person(){
}

var friend = new Person();

/*
重写后
Person.prototype -> New Person Prototype
friend.[[prototype]] -> Person Prototype
*/
Person.prototype = {
    constructor: Person,
    name : "Nicholas",
    age : 29,
    job : "Software Engineer",
    sayName : function () {
        alert(this.name);
    }
};

friend.sayName();   //error
friend.__proto__.constructor.prototype.sayName(); //Nicolas
# 原型模式的问题:

修改一个实例也会同时修改另一个实例

# 6.2.4 构造函数 + 原型模式

创建自定义类型的最常见方式,就是组合使用构造函数与原型模式。构造函数用于定义实例属性,原型用于定义方法和共享属性。

function Person(name, age, job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.friends = ["Shelby", "Court"];
}

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("Van");

alert(person1.friends);    //"Shelby,Court,Van"
alert(person2.friends);    //"Shelby,Court"
alert(person1.friends === person2.friends);  //false
alert(person1.sayName === person2.sayName);  //true

# 6.3 继承

# 6.3.1 原型链

原型和原型链的关系 instance.constructor.prototype = instance.__proto__

function SuperType() {
	this.property = true;
}

SuperType.prototype.getSuperValue = function() {
	return this.property;
};

function SubType() {
	this.subproperty = false;
}

//inherit from SuperType
//原型 继承 原型
SubType.prototype = new SuperType();

SubType.prototype.getSubValue = function() {
	return this.subproperty;
};

var instance = new SubType();

6/Untitled.png

# 完整的原型链:

instance 是一个实例 他的proto等于 SubType.prototype

instance.proto → subType Prototype = subType.prototype

subType Prototype.proto → superType Prototype = superType.prototype

superType Prototype.proto → Object Prototype = Object.prototype

# 谨慎定义方法:

覆盖超类的某个方法或添加超类不存在的方法时,一定要放在替换原型语句之后。

//inherit from SuperType
SubType.prototype = new SuperType();

//new method
SubType.prototype.getSubValue = function (){
    return this.subproperty;
};

//override existing method
SubType.prototype.getSuperValue = function (){
    return false;
};

不要使用对象字面量创建原型方法,会重写原型链。

SubType.prototype = {
    getSubValue : function (){
        return this.subproperty;
    },

    someOtherMethod : function (){
        return false;
    }
};

var instance = new SubType();
alert(instance.getSuperValue());   //error!

# 6.3.2 借用构造函数

function SuperType(){
    this.colors = ["red", "blue", "green"];
}

function SubType(){
}

//inherit from SuperType
SubType.prototype = new SuperType();

var instance1 = new SubType();
instance1.colors.push("black");
alert(instance1.colors);    //"red,blue,green,black"

var instance2 = new SubType();
alert(instance2.colors);    //"red,blue,green,black"

# 6.3.3 组合继承

使用原型链实现对原型属性和方法的继承,通过借用构造函数来实现对实例属性的继承。

// 父类
// 构造函数
function SuperType(name) {
  this.name = name;
  this.colors = ["red", "blue", "green"];
}
// 原型链
SuperType.prototype.sayName = function() {
  alert(this.name);
};

// 子类继承
// 构造函数
function SubType(name, age) {
  SuperType.call(this, name);

  this.age = age;
}

// 原型链
SubType.prototype = new SuperType();

SubType.prototype.sayAge = function() {
  alert(this.age);
};

var instance1 = new SubType("Nicholas", 29);
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"
instance1.sayName(); //"Nicholas";
instance1.sayAge(); //29

var instance2 = new SubType("Greg", 27);
alert(instance2.colors); //"red,blue,green"
instance2.sayName(); //"Greg";
instance2.sayAge(); //27
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

# 第十三章 事件

JavaScript 与 HTML 之间的交互是通过事件实现的。
可以使用侦听器来预订事件,以便事件发生时执行相应的代码。(观察员模式)

# 13.1 事件流

事件流描述的是从页面中接收事件的顺序

冒泡 捕获 DOM
内->外 外->内 外->内->外

# 13.1.1 事件冒泡

IE 的事件流叫做事件冒泡

事件开始时由最具体的元素(嵌套层次最深的那个节点)接收,然后逐级向上传播到较为不具体的节点。

事件冒泡 :<div> -> <body> -> <html> -> document

所有现代浏览器都支持事件冒泡,但在具体实现上还是有一些差别。

# 13.1.2 事件捕获

Netscape Communicator 提出的另一种事件流叫事件捕获

事件捕获 :document -> <html> -> <body> -> <div>

# 13.1.3 DOM 事件流

DOM2 级事件流规定事件流包含三个阶段:

  1. 事件捕获阶段
  2. 处于目标阶段
  3. 事件冒泡阶段

整个流程:

事件捕获 处于目标 事件冒泡
document->html->body div div->body->html->document

# 13.2 事件处理程序

事件就是用户或浏览器自身执行的某种动作。响应某个事件的函数就叫做事件处理程序(或事件侦听器)

事件的名字 事件处理程序的名字
click/load onclick/onnload
DOM2✅ HTML✅ DOM0✅ IE✅

# 13.2.1 HTML 事件处理程序

用一个 HTML 特性来指定 嵌入在标签中

<input type="button" value="Click" onclick="alert(this.value)" />
1
  • this 值等于事件的目标元素
  • 很多 HTML 事件处理程序会被封装在一个 try-catch 块中

# 13.2.2 DOM 0 级 事件处理程序

每个元素都有自己的事件处理程序属性,通常全部小写,例如 onclick 将这种属性的值设置为一个函数即可指定事件处理程序

var btn = document.getElementById("myBtn");
btn.onclick = function() {};
1
2
  • DOM 0 级 指定的事件处理程序被认为是元素的方法

# 13.2.3 DOM 2 级 事件处理程序

DOM 2 级事件定义了两个方法:addEventListener()和 removeEventListener()

btn.addEventListener("click", handler, false);
1
  • true = 捕获 / false = 冒泡 (默认)
  • 通过 add 添加的事件处理程序只能通过 remove 移除

# 13.2.4 IE 事件处理程序

IE 实现了类似 DOM 中的方法 attachEvent()/detachEvent()

btn.attachEvent("onclick", handler);
1

与 DOM 的区别:attachEvent()的 this = window!!

# 13.2.5 跨浏览器 事件处理程序

依次判断顺序:

  1. DOM2
  2. IE
  3. DOM0
// 参数:注册元素,事件种类,事件处理程序
function addHandler(ele, type, handler) {
  if (element.addEventListener) {
    element.addEventListner(type, handler, false);
  } else if (element.attachEvent) {
    element.attachEvent("on" + type, handler);
  } else {
    element["on" + type] = handler;
  }
}
1
2
3
4
5
6
7
8
9
10

# 13.3 事件对象

在触发 DOM 上某个事件时,会产生一个事件对象 event,包含着与事件有关的信息。包括导致事件的元素、事件的类型及其他与特定事件相关的信息。

# 13.3.1 DOM 中的事件对象

兼容 DOM 的浏览器会将 event 对象传入事件处理程序中.

var btn = document.getElementById("myBtn");
btn.onclick = function(e) {};
btn.addEventListener("click", e => {}, false);
1
2
3
  • this / event.currentTarget 都指向注册元素
  • event.target 指向目标元素
  • preventDefault():如阻止链接被点击会跳转等默认行为
  • stopPropagation():立刻停止事件在 DOM 层次的传播
  • event.eventPhase = 1/捕获 2/处于目标 3/冒泡

event 对象只有事件处理程序执行期间才会存在

# 13.3.2 IE 中的事件对象

# 13.3.3 跨浏览器 的事件对象


# 13.4 事件类型


# 13.5 内存和性能

i