Eloquent JavaScript #06# class
索引
Notes
1、this
① 隐式传入this参数
function speak(line) {
console.log(`The ${this.type} rabbit says '${line}'`);
}
let whiteRabbit = {type: "white", speak};
let hungryRabbit = {type: "hungry", speak};
② 通过call显式传入this参数
speak.call(hungryRabbit, "Burp!");
③ 对于箭头函数而言this指的是外围函数的this
function normalize() {
console.log(this.coords.map(n => n / this.length));
}
normalize.call({coords: [0, 2, 3], length: 5});
// → [0, 0.4, 0.6]
用function关键词定义该函数然后传入,或者把箭头函数独立定义在外面,上面的代码都不会正常运行:
const f = n => n / this.length; function normalize() {
console.log(this.coords.map(f));
}
normalize.call({coords: [0, 2, 3], length: 5});
// → [NaN, Infinity, Infinity]
2、原型Prototype
对象接到调用后首先搜索自身的属性,找不到就在原型中查找。
js中的原型构成一个三角关系,最根部是Object.prototype,而Function.prototype和Array.prototype则是两个分支。
console.log(Object.getPrototypeOf({}) ==
Object.prototype);
// → true
console.log(Object.getPrototypeOf(Object.prototype));
// → null
console.log(Object.getPrototypeOf(Math.max) ==
Function.prototype);
// → true
console.log(Object.getPrototypeOf([]) ==
Array.prototype);
// → true
可以用Object.create创捷具有特定原型的对象:
let protoRabbit = {
speak(line) {
console.log(`The ${this.type} rabbit says '${line}'`);
}
};
let killerRabbit = Object.create(protoRabbit);
killerRabbit.type = "killer";
killerRabbit.speak("SKREEEE!");
// → The killer rabbit says 'SKREEEE!'
在上面的例子中,speak方法是所有兔子共有的,而type属性则是杀手兔子私有的。
3、类
在java中兔子类的构造方法应该是这样的:
public class Rabbit {
public String type;
public void speak() {}
// 构造器
public Rabbit(String type) {
this.type = type;
}
}
然而在js里却要这样实现:
function makeRabbit(type) {
let rabbit = Object.create(protoRabbit);
rabbit.type = type;
return rabbit;
}
正常的兔子都应该通过该函数实例化,这样才可以保证兔子们共享speak,又具备自己独立的type。
js提供了一种方式让定义这类函数更加简单:
function Rabbit(type) {
this.type = type;
}
Rabbit.prototype.speak = function(line) {
console.log(`The ${this.type} rabbit says '${line}'`);
}; let weirdRabbit = new Rabbit("weird");
事实上,js中的所有函数都具有一个prototype属性,它是一个派生自Object.prototyp的空对象。你可以用其它对象覆盖它,或者像上面那样改造它。构造函数、构造函数的prototype
属性指向的对象、通过构造函数创建的对象之间的关系如下:
PS. 独家制作↑
4、class符号(since 2015)
类实质上就是一个函数,而函数其实也是个对象。
class Rabbit {
constructor(type) {
this.type = type;
}
speak(line) {
console.log(`The ${this.type} rabbit says '${line}'`);
}
} let killerRabbit = new Rabbit("killer");
let blackRabbit = new Rabbit("black");
相对于3只是换了一种书写方式,且在class定义中只允许添加函数到原型。
匿名类:
let obj = new class {
constructor(word) {
this.word = word;
}
speak() {
console.log('speak.');
}
} ("hello"); obj.word;
// → "hello"
obj.speak();
// → speak.
5、覆盖派生属性
类似java,接收到调用之后,js总是搜索当前对象,找不到才会逐个地搜索上一级原型对象,因此,如果子对象和父对象有同名的属性,那么父对象属性理所当然要被子对象的属性“覆盖”掉。
6、Maps
如果用对象实现map:
let obj = {
a: "abc",
b: "bca",
c: "cab"
};
然而它存在一些问题:
let obj = {
a: "abc",
b: "bca",
c: "cab"
}; console.log("a" in obj);
// -> true
console.log("toString" in obj);
// -> true
原型对象的属性也会被作为键。针对这个问题,有两个比较容易的解决方案——创建没原型的对象:
let obj = Object.create(null);
obj.a = "abc"; console.log("a" in obj);
// -> true
console.log("toString" in obj);
// -> false
或者用Object.keys或者hasOwnProperty代替in,它们都不会把原型属性包含在范畴内。
不过还存在一个问题,对象的属性只能是字符串,这就意味着没有办法用对象作为键,因此最佳的方案还是采用js内建的Map类:
let myMap = new Map(); let obj1 = {};
let obj2 = {};
let obj3 = null; myMap.set(obj1, "a");
myMap.set(obj2, "b");
myMap.set(obj3, "c"); console.log(myMap.get(obj1));
// → a
console.log(myMap.has(obj2));
// → true
console.log(myMap.get(obj3));
// → c
console.log(myMap.get(null));
// → c
7、Symbols
参考文章:js-ES6学习笔记-Symbol
let sym = Symbol("name");
console.log(sym == Symbol("name"));
// → false
Rabbit.prototype[sym] = 55;
console.log(blackRabbit[sym]);
// → 55
/ 示例2
const toStringSymbol = Symbol("toString");
Array.prototype[toStringSymbol] = function() {
return `${this.length} cm of blue yarn`;
}; console.log([1, 2].toString());
// → 1,2
console.log([1, 2][toStringSymbol]());
// → 2 cm of blue yarn
/ 示例3
let stringObject = {
[toStringSymbol]() { return "a jute rope"; }
};
console.log(stringObject[toStringSymbol]());
// → a jute rope
/ 示例4
let sy1 = Symbol("just a description");
let sy2 = Symbol("just a description"); console.log(sy1 == sy2);
// → false let obj = {
sy1: "a",
[sy1]: "b",
[sy2]: "c",
sy3: Symbol("just a description")
}; console.log(obj.sy1);
// → a
console.log(obj["sy1"]);
// → a
console.log(obj[sy1]);
// → b
console.log(Object.keys(obj));
// → ["sy1", "sy3"]
在你仅仅只是需要一个独一无二的符号而不论它是什么的时候可以用它。
优点:降低代码(由于语义产生的)耦合性。
注意点:symbol作为属性,必须像上面那样用方括号包含对symbol的绑定进行声明(方括号会导致计算),并且只能用方括号+对symbol的绑定访问那个属性。
8、iterator接口
所有可迭代(可以通过for
/of
遍历)对象其实都包含一个由Symbol.iterator定义的方法。该方法返回一个实现了next方法的(iterator)对象。
可以直接调用这个方法拿到这种对象:
let okIterator = "OK"[Symbol.iterator]();
console.log(okIterator.next());
// → {value: "O", done: false}
console.log(okIterator.next());
// → {value: "K", done: false}
console.log(okIterator.next());
// → {value: undefined, done: true}
自定义实现该接口的矩阵对象(课本实例):
class Matrix {
constructor(width, height, element = (x, y) => undefined) {
this.width = width;
this.height = height;
this.content = []; for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
this.content[y * width + x] = element(x, y);
}
}
} get(x, y) {
return this.content[y * this.width + x];
}
set(x, y, value) {
this.content[y * this.width + x] = value;
}
} class MatrixIterator {
constructor(matrix) {
this.x = 0;
this.y = 0;
this.matrix = matrix;
} next() {
if (this.y == this.matrix.height) return {done: true}; let value = {x: this.x,
y: this.y,
value: this.matrix.get(this.x, this.y)};
this.x++;
if (this.x == this.matrix.width) {
this.x = 0;
this.y++;
}
return {value, done: false};
}
} Matrix.prototype[Symbol.iterator] = function() {
return new MatrixIterator(this);
}; let matrix = new Matrix(2, 2, (x, y) => `value ${x},${y}`);
for (let {x, y, value} of matrix) {
console.log(x, y, value);
}
// → 0 0 value 0,0
// → 1 0 value 1,0
// → 0 1 value 0,1
// → 1 1 value 1,1
9、Getters, setters, and statics
class Temperature {
constructor(celsius) {
this.celsius = celsius;
}
get fahrenheit() {
return this.celsius * 1.8 + 32;
}
set fahrenheit(value) {
this.celsius = (value - 32) / 1.8;
} static fromFahrenheit(value) {
return new Temperature((value - 32) / 1.8);
}
} let temp = new Temperature(22);
console.log(temp.fahrenheit);
// → 71.6
temp.fahrenheit = 86;
console.log(temp.celsius);
// → 30
Temperature.fromFahrenheit(100)
10、继承
class SymmetricMatrix extends Matrix {
constructor(size, element = (x, y) => undefined) {
super(size, size, (x, y) => {
if (x < y) return element(y, x);
else return element(x, y);
});
} set(x, y, value) {
super.set(x, y, value);
if (x != y) {
super.set(y, x, value);
}
}
} let matrix = new SymmetricMatrix(5, (x, y) => `${x},${y}`);
console.log(matrix.get(2, 3));
// → 3,2
封装和多态可以将增强代码的独立性,而继承却强化了类之间的关系,制造更多的纠纷。当采用继承的时候,你必须知道它是如何工作的,而不仅仅简单的去用它。继承是有用的工具,但不应当作为解决问题的首选。
11、instanceof
function X() { } let obj = new X(); console.log(obj instanceof Object);
// → true
console.log(obj instanceof X);
// → true
Exercises
① A vector type
class Vec {
constructor(x, y) {
this.x = x;
this.y = y;
} plus(v) {
return new Vec(v.x + this.x, v.y + this.y);
} minus(v) {
return new Vec(this.x - v.x, this.y - v.y);
} get length() {
return Math.sqrt(this.x * this.x + this.y * this.y);
}
} console.log(new Vec(1, 2).plus(new Vec(2, 3)));
// → Vec{x: 3, y: 5}
console.log(new Vec(1, 2).minus(new Vec(2, 3)));
// → Vec{x: -1, y: -1}
console.log(new Vec(3, 4).length);
// → 5
- -- - - - -- - - - - -- - - - - -- - - - -- - - - - -- - - - -- - - - - -- - - - -- - - -
② Groups
class Group {
// Your code here.
constructor() {
this.container = [];
} static from(iterable) {
let group = new Group();
for (let x of iterable) {
group.add(x);
}
return group;
} add(x) {
if (!this.container.includes(x)) {
this.container.push(x);
}
} delete(x) {
this.container = this.container.filter(a => !(a === x));
} has(x) {
return this.container.includes(x);
}
} let group = Group.from([10, 20]);
console.log(group.has(10));
// → true
console.log(group.has(30));
// → false
group.add(10);
group.delete(10);
console.log(group.has(10));
// → false
- -- - - - -- - - - - -- - - - - -- - - - -- - - - - -- - - - -- - - - - -- - - - -- - - -
③ Iterable groups
实践证明,class和function一样可以无视在文本中定义的位置。
class Group {
constructor() {
this.container = [];
} static from(iterable) {
let group = new Group();
for (let x of iterable) {
group.add(x);
}
return group;
} add(x) {
if (!this.container.includes(x)) {
this.container.push(x);
}
} delete(x) {
this.container = this.container.filter(a => !(a === x));
} has(x) {
return this.container.includes(x);
} [Symbol.iterator]() {
return new GroupIterator(this);
}
} class GroupIterator {
constructor(x) {
this.index = 0;
this.container = x.container;
} next() {
if (this.index === this.container.length) return {done: true};
let value = this.container[this.index++];
return {value, done: false};
}
} for (let value of Group.from(["a", "b", "c"])) {
console.log(value);
}
// → a
// → b
// → c
- -- - - - -- - - - - -- - - - - -- - - - -- - - - - -- - - - -- - - - - -- - - - -- - - -
④ Borrowing a method
let map = {one: true, two: true, hasOwnProperty: true}; // Fix this call
console.log(Object.hasOwnProperty.call(map, "one"););
// → true
Eloquent JavaScript #06# class的更多相关文章
- Eloquent JavaScript #13# HTTP and Forms
索引 Notes fetch form focus Disabled fields form’s elements property 阻止提交 快速插入单词 实时统计字数 监听checkbox和rad ...
- Eloquent JavaScript #11# The Document Object Model
索引 Notes js与html DOM 在DOM树中移动 在DOM中寻找元素 改变Document 创建节点 html元素属性 布局 style CSS选择器 动画 Exercises Build ...
- Eloquent JavaScript #10# Modules
索引 Notes 背景问题 模块Modules 软件包Packages 简易模块 Evaluating data as code CommonJS modules ECMAScript modules ...
- Eloquent JavaScript #04# Objects and Arrays
要点索引: JSON More ... 练习 1.补:js字符串的表达方式有三种: "" 和 '' 没什么区别,唯一区别在于 "" 中写 "要转义字符 ...
- Eloquent JavaScript #03# functions
索引: let VS. var 定义函数的几种方式 more... 1.作者反复用的side effect side effect就是对世界造成的改变,例如说打印某些东西到屏幕,或者以某种方式改变机器 ...
- Eloquent JavaScript #02# program_structure
第一章中作者介绍了各种值,但是这些独立的值是没有意义的,只有当值放在更大的框架的时候才会彰显它们的价值.所以第二章开始介绍程序结构. 1.var VS. let 以及 const 作者推荐用 let ...
- Eloquent JavaScript #01# values
When action grows unprofitable, gather information; when information grows unprofitable, sleep. ...
- Eloquent JavaScript #12# Handling Events
索引 Notes onclick removeEventListener Event objects stopPropagation event.target Default actions Key ...
- Eloquent JavaScript #09# Regular Expressions
索引 Notes js创建正则表达式的两种方式 js正则匹配方式(1) 字符集合 重复匹配 分组(子表达式) js正则匹配方式(2) The Date class 匹配整个字符串 Choice pat ...
随机推荐
- nginx的访问控制
一.基于Basic Auth认证与基于IP的访问控制 一.基于Basic Auth认证 Nginx提供HTTP的Basic Auth功能,配置了Basic Auth之后,需要输入正确的用户名和密码之后 ...
- (转)Springboot日志配置(超详细,推荐)
Spring Boot-日志配置(超详细) 更新日志: 20170810 更新通过 application.yml传递参数到 logback 中. Spring Boot-日志配置超详细 默认日志 L ...
- GRU门控制循环单元【转载】
转自:https://www.infoq.cn/article/sliced-recurrent-neural-networks 1.门控循环单元 GRU GRU 由 reset gate r 和 u ...
- word2vec 评测 window_different
This is a test for word2vecWed Nov 07 16:04:39 2018dir of model1: ./model/window3_ min_count2_worker ...
- IOC解耦-面向接口编程的优点
原文:https://blog.csdn.net/jj_nan/article/details/70161086 参考:https://www.cnblogs.com/landeanfen/p/477 ...
- 一个基于JRTPLIB的轻量级RTSP客户端(myRTSPClient)——实现篇:(九)以g711-mulaw为例添加新的编码格式解析支持
一.myRtspClient音频解析架构 AudioTypeBase是处理解析各种编码的音频数据的接口类.处理MPA数据的MPEG_Audio类和处理g711-mulaw的PCMU_Audio类均从A ...
- 产品设计教程:wireframe,prototype,mockup到底有何不同?
wireframe,prototype,mockup 三者经常被混用,很多人把三者都叫原型,真的是这样吗? 我们来看看三者到底有何不同.先来做一道选择题: 从这张图可以看出,prototype 和其他 ...
- node加密解密 crytpo
var crypto = require('crypto'); exports.encrypt = function (str, secret) { var cipher = crypto.creat ...
- nodejs+mysql入门实例(链接到数据库)
//连接数据库 var mysql = require('mysql'); var connection = mysql.createConnection({ host: '******', //数据 ...
- iOS 设计模式-NSNotificationCenter 通知中心
通知介绍 每一个应用程序都有一个通知中心(NSNotificationCenter)实例,专门负责协助不同对象之间的消息通信 任何一个对象都可以向通知中心发布通知(NSNotification),描述 ...