阅读笔记-JavaScript高级程序设计第四版(一)

50 篇文章 1 订阅

JavaScript高级程序设计第四版 目录


前言

本文章来源为:《JavaScript高级程序设计》(第四版),如有机会,请以书中内容为主。

注意

  • 此文不涉及基础语法,如有需要可观看菜鸟教程

个人体会

第四版这本书啃起来有点难啃。迭代器,原型链,promise还是有一定了解才行,否则阅读起来有点生硬。
node毕竟是单线程非阻塞的语言,为了模拟多线程,会出现async和await这种东西,精髓是在回调函数上。

推荐

  • 《JavaScript高级程序设计》(第四版)
  • 《JavaScript学习指南》

1. 什么是JavaScript

DOm

文档对象模型(DOM):是一个应用编程接口(API)

<html>
<head>
	<title>xxx</title>
</head>
<body>
<p>xxxx</p>
</body>
</html>

图例:
在这里插入图片描述

2. HTML中的JavaScript

<script>元素

属性

  • async:立刻下载脚本
  • charset:指定的代码字符集
  • crossorigin:跨资源共享设置
  • defer:在文档解析和显示完成后再执行脚本
  • integrity:允许比对接收到的资源和指定的加密签名,已验证子资源的完整性。用于CDN
  • language
  • src
  • type:代码块中脚本语言的内容类型。如果是module,则被当作ES6模块

动态加载脚本

let script = document.createElement('script');
script.src = 'test.js'
document.head.appendChild(script);

XHTML中的变化

XHTML是将HTML作为XML重新包装的结果。

区别

  • XHTML使用JavaScript必须指定type=text/javascript
  • 规则严格
  • 已经废弃
  • //<!CDATA //]]>

3. 语言基础

内容概括

  • 语法
  • 数据类型
  • 控制语句
  • 函数

此文不涉及基础语法,可观看菜鸟教程

严格模式

  • 处理不规范写法
  • use strict

Symbol类型

ECMAScript6新增数据类型

  • 符号是原始值
  • 符号实例时唯一、不可变的
  • 用于确保对象属性使用唯一标识符,不会出现冲突

符号的基本用法

Symbol()函数初始化
let sym = Symbol();
console.log(typeof sym);	// symbol
传入字符串对符号描述
let genericSymbol = Symbol();
let otherGenericSymbol = Symbol();

let foolSymbol = Symbol('foo');
let otherFoolSymbol = Symbol('foo');

console.log(genericSymbol == otherGenericSymbol);
console.log(foolSymbol == otherFoolSymbol);
Symbol函数不能用作构造函数
  • 不能和new一起使用

全局符号注册表 - symbol.for

如果运行时的不同部分需要共享和重用符号实例:

  • 用一个字符串作为键
  • 全局符号注册表中创建并重用符号
  • Symbol.for()方法
// 创建新符号
let fooGlobalSymbol = Symbol.for('foo');
// 重用已有符号
let otherFooGlobalSymbol = Symbol.for('foo');

console.log(fooGlobalSymbol === otherFooGlobalSymbol);  // true
symbol.for
  • 对每个字符串键都执行幂等操作
  • 第一次使用某个字符串调用时,会检查全局运行时注册表
  • 如果不存在对应的符号,会生成一个新符号实例并添加到注册表中
  • 后续使用相同的字符串调用时,会先检查注册表,发现对应的符号返回该符号实例
  • 全局注册表中的符号必须为字符串,任何值都会被转换为字符串
  • 类似于设计模式中的单例模式

  • symbol与symbol.for定义的结果是不同的
let test1 = Symbol('foo');
let test2 = Symbol.for('foo');

console.log(test1 === test2);   // false
查询全局注册表 - symbol.keyFor
  • 接收符号,返回该全局符号对应的字符串键
  • 如果查询的不是全局符号,则会返回undefined
// create global symbol
let s = Symbol.for('foo');
console.log(Symbol.keyFor(s));  // foo

// create ordinary symbol
let s2 = Symbol('bar');
console.log(Symbol.keyFor(s2)); // undefined

// type error
console.log(Symbol.keyFor(123));

使用符号作为属性

可以用字符串或者数值作为属性的地方,都可以使用符号。
包括:

  • 对象字面量属性,只能在计算属性语法中使用符号作为属性
  • Object.defineProperty()
  • Object.definedProperties()
let s1 = Symbol('foo'),
    s2 = Symbol('bar'),
    s3 = Symbol('baz'),
    s4 = Symbol('qux');

let o = {
    [s1]: 'foo val'
};

console.log(o);

o[s2] = 'foo val2';
console.log(o);

在这里插入图片描述

获取对象实例的四种用法
  • Object.getOwnPropertyNames():返回对象实例的常规属性数组
  • Object.getOwnPropertySymbols():返回对象实例的符号属性数组
  • Object.getOwnPropertyDescriptors():返回同时包含常规和符号属性描述符的对象
  • Reflect.ownKeys():返回两种类型的键
console.log(Object.getOwnPropertyNames(o));
console.log(Object.getOwnPropertySymbols(o));
console.log(Object.getOwnPropertyDescriptor(o));
console.log(Reflect.ownKeys(o));
注意
  • 符号属性是对内存中符号的一个引用
  • 直接创建并用作属性的符号不会丢失
  • 如果没有显式地保存这些属性的引用,必须遍历对象的所有符号属性才能找到响应的属性键
let o = {
    [Symbol('foo')]: 'foo val',
    [Symbol('bar')]: 'bar val'
};

console.log(o);

let barSymbol = Object.getOwnPropertySymbols(o)
                .find((symbol) => 
                    symbol.toString().match(/bar/)
                );

console.log(barSymbol);

常用内置符号

  • 用于暴露语言内部行为,可以直接访问、重写或模拟这些行为
  • 都以Symbol工厂函数字符串属性的形式存在
  • 常用于重新定义,改变原生结构的行为
注意
  • 引用符号在规范中的名称,前缀为@@,如@@iterator指的就是Symbol.iterator

Symbol.asyncIterator

  • 该方法返回对象默认的AsyncIterator,由for-await-of使用
  • 这个符号表示实现异步迭代器API的函数
  • for-await-of循环时,执行异步迭代操作,调用以Symbol.asyncIterator为键的函数,返回一个实现迭代器的API对象
  • 对象是实现API的AsyncGenerator
class Foo {
    async *[Symbol.asyncIterator]() {}
}

let f = new Foo();

console.log(f[Symbol.asyncIterator]());	// Object [AsyncGenerator] {}
  • 这个由Symbol.asyncIterator生成的对象应该通过其next()方法陆续返回Promise实例
  • 既可以通过显示调用next()方法返回,也可以通过隐式调用异步生成器函数返回
class Emitter {
    constructor(max) {
        this.max = max;
        this.asyncIdx = 0;
    }
    // 异步迭代器的API
    async *[Symbol.asyncIterator] () {
        while(this.asyncIdx < this.max) {
            yield new Promise((resolve) => resolve(this.asyncIdx++));
        }
    }
}

async function asyncCount() {
    let emitter = new Emitter(5);

    // 循环时,异步迭代操作
    for await(const x of emitter){
        console.log(x);
    }
}

asyncCount();
注意
  • symbol.asyncIterator只支持ES2018规范

Symbol.hasInstance

  • 该方法决定一个构造器对象是否认可一个对象时它的实例,由instanceof操作符使用
  • instanceof操作符可以确定一个对象实例的原型链上是否有原型
function Foo() {}
let f = new Foo();
console.log(f instanceof Foo);  // true

class Bar{}
let b = new Bar();
console.log(b instanceof Bar);  // true
  • instanceof会使用Symbol.hasInstance确定关系
Symbol.hasInstance为键
function Foo() {}
let f = new Foo();
console.log(Foo[Symbol.hasInstance](f));  // true

class Bar{}
let b = new Bar();
console.log(Bar[Symbol.hasInstance](b));  // true
重新定义函数
  • 这个属性定义在function原型上,默认所有函数和类上都可以调用
  • 可以在继承的类上通过静态方法重新定义这个函数
class Bar{};
class Baz extends Bar {
    static [Symbol.hasInstance]() {
        return false;
    }
}

let b = new Baz();
console.log(Bar[Symbol.hasInstance](b));  // true
console.log(b instanceof Bar);  //true
console.log(Baz[Symbol.hasInstance](b));  //false
console.log(b instanceof Baz);  //false

Symbol.isConcatSpreadable

  • 布尔值,如果是true,意味着对象应该用Array.prototype.concat()打平其数组元素
  • Array.prototype.concat()会根据接收到的对象类型选择如何将一个类数组对象拼接成数组实例
  • false: 默认追加到数组末尾
  • true: 被打平到数组实例
  • 其他不是类数组对象的对象,在Symbol.isConcatSpreadable被设置为true的情况下忽略
let initial = ['foo'];

let array = ['bar'];
// false
console.log(array[Symbol.isConcatSpreadable]);  // undefined
console.log(initial.concat(array));  // ['foo', 'bar']
array[Symbol.isConcatSpreadable] = false;
console.log(initial.concat(array));  //['foo', Array(1)]
// true
let arrayLikeObject = { length: 1, 0: 'baz'};
console.log(arrayLikeObject[Symbol.isConcatSpreadable]);  //undefined
console.log(initial.concat(arrayLikeObject));  //['foo', {...}]
arrayLikeObject[Symbol.isConcatSpreadable] = true;
console.log(initial.concat(arrayLikeObject));  //['foo', 'baz']
// set
let otherObject = new Set() . add('qux');
console.log(otherObject[Symbol.isConcatSpreadable]);  //undefined
console.log(initial.concat(otherObject));  // ['foo', Set(1)]
otherObject[Symbol.isConcatSpreadable] = true;
console.log(initial.concat(otherObject));  // ['foo']

在这里插入图片描述

Symbol.iterator

  • 该方法返回对象默认的迭代器,由for-of使用
class Foo {
    *[Symbol.iterator]() {}
}

let f = new Foo();

console.log(f[Symbol.iterator]);

在这里插入图片描述

  • 显示next()
  • 隐式生成器函数
class Emitter{
    constructor(max) {
        this.max = max;
        this.idx = 0;
    }

    *[Symbol.iterator]() {
        while(this.idx < this.max){
            yield this.idx++;
        }
    }
}

function count() {
    let emitter = new Emitter(5);

    for (const x of emitter){
        console.log(x);
    }
}

count();

symbol.match

  • 正则表达式法,用正则表达式去匹配字符串
  • String.prototype.match()
  • String.prototype.match()会使用Symbol.match为键的函数来对正则表达式求值
  • 正则表达式的原型上默认有这个函数的定义,因此所有正则表达式实例默认是这个String方法的有效参数
console.log(RegExp.prototype[Symbol.match]);

console.log('foobar'.match(/bar/));

在这里插入图片描述

  • 如果传入非正则表达式值,会导致该值被转换为regexp对象
  • 重新定义symbol.match,可直接使用实例
class FooMatcher {
    static [Symbol.match](target){
        return target.includes('foo');
    }
}

console.log('foobar'.match(FooMatcher));    // true
console.log('barbaz'.match(FooMatcher));    //false

class StringMatcher{
    constructor(str){
        this.str = str;
    }

    [Symbol.match](target){
        return target.includes[this.str];
    }
}

console.log('foobar'.match(new StringMatcher('foo')));  // true
console.log('barbaz'.match(new StringMatcher('qux')));  //false

symbol.replace

  • 替换一个字符串中匹配的子串,由String.prototype.replace()使用
console.log(RegExp.prototype[Symbol.replace]);

console.log('foobarbaz'.replace(/bar/, 'qux'));

在这里插入图片描述

class FooReplacer {
    static [Symbol.replace] (target, replacement) {
        return target.split('foo').join(replacement);
    }    
}

console.log('barfoobaz'.replace(FooReplacer, 'qux'));

class StringReplacer {
    constructor(str){
        this.str = str;
    }
    
    [Symbol.replace](target, replacement){
        return target.split(this.str).join(replacement);
    }
}

console.log('barfoobaz'.replace(new StringReplacer('foo'), 'qux'));

在这里插入图片描述

Symbol.search

  • 返回字符串中匹配正则表达式的索引
class FooSearch {
    static [Symbol.search](target) {
        return target.indexOf('foo');
    }
}

console.log('foobar'.search(FooSearch));    // 0
console.log('barfoo'.search(FooSearch));    // 3
console.log('barbaz'.search(FooSearch));    // -1

class StringSearch {
    constructor(str) {
        this.str = str;
    }

    [Symbol.search](target){
        return target.indexOf(this.str);
    }
}

console.log('foobar'.search(new StringSearch('foo')));  // 0
console.log('barfoo'.search(new StringSearch('foo')));  // 3
console.log('barbaz'.search(new StringSearch('foo')));  // -1

在这里插入图片描述

symbol.species

  • 创建派生对象的构造函数
  • symbol.species定义静态的获取器(getter),覆盖新创建实例的原型定义
class Bar extends Array {}
class Baz extends Array {
    static get [Symbol.species]() {
        return Array;
    }
}

let bar = new Bar();
console.log(bar instanceof Array);  // true
console.log(bar instanceof Bar);    // true

bar = bar.concat('bar');
console.log(bar instanceof Array);  // true
console.log(bar instanceof Bar);    // true

let baz = new Baz();
console.log(baz instanceof Array);  // true
console.log(baz instanceof Baz);    // true

baz = baz.concat('baz');
console.log(baz instanceof Array);  // true
console.log(baz instanceof Baz);    // false

symbol.split

  • 该函数作为创建派生对象的构造函数
class FooSplitter {
    static [Symbol.split](target){
        return target.split('foo');
    }
}

console.log('barfoobaz'.split(FooSplitter));

class StringSplitter {
    constructor(str){
        this.str = str;
    }

    [Symbol.split](target){
        return target.split(this.str);
    }
}

console.log('barfoobaz'.split(new StringSplitter('foo')));

在这里插入图片描述

symbol.toPrimitive

  • 将对象转换为相应的原始值
class Foo {}
let foo = new Foo();

console.log(3 + foo);   
console.log(3 - foo);
console.log(String(foo));

class Bar {
    constructor() {
        this[Symbol.toPrimitive] = function(hint) {
            switch(hint) {
                case 'number':
                    return 3;
                case 'string':
                    return 'string bar';
                case 'default':
                default:
                    return 'default bar';
            }
        }
    }
}

let bar = new Bar();

console.log(3 + bar);
console.log(3 - bar);
console.log(String(bar));

在这里插入图片描述

symbol.toStringTag

  • 该字符串用于创建对象的默认字符串描述
  • Object.prototype.toString()
let s = new Set();

console.log(s);     // Set[0] {}
console.log(s.toString());      // [Object Set]
console.log(s[Symbol.toStringTag]); //Tag

class Foo {}
let foo = new Foo();

console.log(foo);       // Foo {}
console.log(foo.toString());    // [object Object]
console.log(foo[Symbol.toStringTag]);   // undefined

class Bar {
    constructor(){
        this[Symbol.toStringTag] = 'Bar';
    }
}
let bar = new Bar();

console.log(bar);       // Bar {}
console.log(bar.toString());    // [object object]
console.log(bar[Symbol.toStringTag]);   // bar

在这里插入图片描述

Object

  • 对象其实就是一组数据和功能的集合
  • let o = new Object()
  • Object也是派生其他类的基类,Object类所有属性和方法在派生的对象上同样存在

每个Object类的属性和方法

  1. constructor
  2. hasOwnProperty(propertyName):判断当前对象实例是否存在给定属性
  3. isPrototypeof(object):判断当前对象是否为另一个对象的原型
  4. propertyIsEnumerable(propertyName):判定给定的属性是否可以使用
  5. toLocaleString():返回对象的字符串表示
  6. toString()
  7. valueOf()

for-in 语句

  • 严格的迭代语句,用于枚举对象中的非符号键属性
  • for (property in expression) statement

for-of 语句

  • 迭代语句,遍历可迭代对象的元素
  • for (property of expression) statement

with语句

  • 将代码作用域设置为特定的对象
  • with (expression) statement
  • 主要是针对一个对象反复操作
let qs = location.search.substring(1);
let hostname = location.hostname;
let url = location.href;

with(location) {
    let qs = search.substring(1);
    let hostname = hostname;
    let url = href;
}
  • 严格模式不允许使用with

严格模式的限制

  1. 函数不能以eval或者arguments作为名称
  2. 函数的参数不能叫做eval或者arguments
  3. 两个函数的参数不能教同一个名称

**如果违反以上规则,将会导致语法错误,代码也不会运行**

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值