前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >《JavaScript高级程序设计》读书笔记

《JavaScript高级程序设计》读书笔记

作者头像
心谭博客
发布2020-04-20 17:18:29
1.1K0
发布2020-04-20 17:18:29
举报
文章被收录于专栏:YuanXinYuanXin

整理自:《JavaScript 高级程序设计》。

第一章 JavaScript 简介

1.2 js 实现

  1. 一个完成的 js 实现由 3 个不同部分组成:核心(ECMAScript)、文档对象模型(DOM)、浏览器对象模型(BOM)
  2. 常见的 Web 浏览器知识 ECMAScript 实现的宿主环境之一,其他环境包括 Node、Adobe Flash
  3. DOM:是针对 XML 经过扩展用于 HTML 的程序编程 API
  4. BOM:控制浏览器显示的页面以外部分

第二章 在 HTML 中使用 JavaScript

2.1 script标签

下面两个属性可以控制 script 加载,它们不能严格保证执行顺序:

  1. async:不阻塞页面,下载并且执行脚本
  2. defer:脚本延迟到文档被完全解析和显示后再执行。

script 脚本中不要嵌入出现"" 字符串,会被错误识别为结束标签。正确写法是:"<\/script>"

如果 script 标签中既有代码内容,并且也引入了外部脚本(src 属性)。浏览器只会执行外部脚本。

2.2 可扩展超文本标记语言 XHTML

XHTML 编写比 HTML 更严格,例如 > 等符号都需要转义。为了保证 js 正常运行,用 CDATA 来包裹。

下面代码在不兼容 xml 的浏览器可以平稳退化:

代码语言:javascript
复制
<script type="text/javascript">
    //  数组检测
    function compare(a, b) {
        if (a < b) {
            console.log("a is less than b");
        }
    }
    //]]>
</script>

2.4 noscript 元素

对于不支持 js 的浏览器,此标签可平稳退化。

代码语言:javascript
复制
<body>
    <noscript>
        <p>请启用JavaScript</p>
    </noscript>
</body>

第三章 基本概念

3.1 语法

针对 ES3 的不确定行为,ES5 增加了严格模式,它是“编译指示”,用来告知 Js 引擎切换到严格模式,需要在代码顶部添加:"use strict";

3.2 关键字和保留字

保留字是之后可能被用作关键字的标识符。比如super,在 es6 中被用在了子类的构造函数中。

3.3 变量

var 声明的变量存在声明提升,如下:

代码语言:javascript
复制
var a = 1;

function test() {
    console.log(a);
    var a = 2;
    a = 3;
}

test();

由于变量声明提升以及函数作用域,相当于以下代码:

代码语言:javascript
复制
var a = 1;

function test() {
    var a = undefined;
    console.log(a);
    a = 2;
    a = 3;
}

test();

因此,输出结果是 undefined

3.4 类型

3.4.1 null 和 undefined

nullundefined 不相同,区别如下:

  • null:空对象指针,typeof null 返回 "object",常用于定义空变量
  • undefined:未定义,变量只声明时,默认赋值undefined
3.4.2 8 和 16 进制

number类型:

  • 8 进制:0 开头,例如 070
  • 16 进制:0x 开头,例如 0x1f
  • 科学计数法:1ex,例如 1e2 = 100

所有 8 和 16 进制值在运算时,都会被转化为 10 进制。

3.4.3 特殊数字

Number.MIN_VALUENumber.MAX_VALUE 分别返回最小值和最大值。超出范围的会被转化为 Infinity

不合法的数,比如 1/0 ,会返回 NaN,需要用 isNaN 判断。对于对象,isNaN 先调用 valueOf ,再掉用 toString

代码语言:javascript
复制
const validNum = {
    valueOf: function() {
        return 1;
    },
    toString: function() {
        return "str";
    }
};

console.log(isNaN(validNum)); // output: false
3.4.4 字符串转数字

parseInt 应该在第二个参数指明进制。

3.4.5 字符串

字符串变量的值是不可变的,当改变值时,会销毁之前的字符串,然后用包含新值的字符串填充变量。

调用数值的 toString 方法,给定参数代表进制。

特殊编码:

  • \xnn:以 16 进制代码 nn 表示字符
  • \unnnn:以 16 进制代码 nnnn 表示 Unicode 字符
代码语言:javascript
复制
console.log("\x41"); // A
console.log("\u03a3"); // Σ
3.4.6 Object 类型

Object 实例都有以下属性:

  • constructor: 指向创建对象的函数
  • hasOwnProperty
  • obj1.isPrototypeOf(obj2): obj1 是不是在 obj2 的原型链上
  • propertyIsEnumerable(propName): propName 能否用 for-in 枚举

关于 isPrototypeOf:

代码语言:javascript
复制
function Demo() {}
var o = {};

var demo = new Demo();
console.log(o.isPrototypeOf(demo)); // output: false

// 将o放在demo实例的原型链上
demo.__proto__ = Demo.prototype = o;
console.log(o.isPrototypeOf(demo)); // output: true

对于 BOM、DOM 等宿主环境提供的对象,可能并不继承 Object,不具有以上通性。

3.5 操作符

1、位操作

  • ~: 按位非。~110 => 001
  • &: 按位与。
  • |: 按位或。
  • ^: 异或操作。位数相同返回 0,不同返回 1。
  • <<: 左移
  • >>: 默认情况,有符号右移,保留符号位(符合正常逻辑)
  • >>>: 无符号右移,在移动时候忽略符号位。

正因为移动时候忽略符号位,因此例如 -64 = 111111..11100000,负数的补码会被当做正数的二进制码。

2、布尔操作

一般直接使用 !! 进行转化。

3、逗号操作符

代码语言:javascript
复制
var num1 = 1,
    num2 = 2,
    num3 = 3; // 多变量声明

var num = (3, 2, 1); // 从右边开始解析,返回 1
console.log(num); // output: 1

3.6 语句

3.6.5 for-in 语句

精准迭代,枚举对象属性。但是效率很低,而且输出的属性名的顺序不确定。

在执行前,需要检测对象是否为 null 或者 undefined,否则 es3 会报错。

3.6.6 label 语句

breakcontinue 联合使用,主要用于多层嵌套循环的流程控制。

配合 break,直接跳出指定的 label

代码语言:javascript
复制
var num = 0;
outermost: for (var i = 0; i < 10; ++i) {
    for (var j = 0; j < 10; ++j) {
        if (i === 5 && j === 5) {
            // i, j为5的时候,结束循环
            break outermost;
        }
        ++num;
    }
}
console.log(num); // 55

配合 continue,直接跳出指定的 label

代码语言:javascript
复制
var num = 0;
outermost: for (var i = 0; i < 10; ++i) {
    for (var j = 0; j < 10; ++j) {
        if (i === 5 && j === 5) {
            continue outermost;
        }
        ++num;
    }
}
console.log(num); // 95

开启调试后会发现,当 i 和 j 为 5 的时候,跳到了 outermost,并且保持了 i 和 j 的变量值。

外层循环导致 i 变为 6,j 清零。

3.6.8 with 语句

设置代码作用域到指定对象中,会导致性能下降。

代码语言:javascript
复制
const obj = {
    a: 1
};

with (obj) {
    console.log(a); // 1
}

3.7 函数

arguments 是类数组对象,严格模式下不能重写或者重新定义其中的值。

arguments.callee 指向函数自身,用于编写递归函数。

注意:js 的函数没有重载。ts 可以重载,但是也只是多类型声明,不符合传统意义的函数重载。

第四章 变量、作用域和内存问题

4.1 基本类型和引用类型的值

4.1.2 赋值

复制函数:

代码语言:javascript
复制
var obj1 = new Object(); // obj1 保存的是副本,不过这个副本是指向实例的一个指针
var obj2 = obj1;

ECMAScript 中所有函数的参数都是按值传递,对于复杂类型,副本就是指向它的指针。

4.1.4 检测类型

基本数据类型:typeof;对象类型检测:instanceof

4.2 执行环境和作用域

延长作用域链的情景:

  1. try-catch中的catch:作用域链前端新增错误对象
  2. with:作用域链前端新增指定对象
  3. 函数闭包

4.3 垃圾回收(GC)

4.3.1 标记清除和引用计数

浏览器的实现有两种:

  1. 标记清除:所有变量打标记;去掉环境中变量的标记,以及被环境中变量引用变量的标记;之后,清除还有标记的变量。
  2. 引用计数:跟踪每个变量引用次数,被引用的变量就加 1;如果此变量又取了另一个变量,减 1。
代码语言:javascript
复制
const value = 1; // 引用0
const copy = value; // 引用+1
const obj = {
    copy // 引用 + 1
};
obj.copy = null; // 引用 -1
// 最后,引用次数为1

引用计数无法处理“循环引用”的情况,例如:

代码语言:javascript
复制
function problem() {
    const obja = {},
        objb = {};

    obja.prop = objb; // objb的引用次数和obja的引用次数都+1
    objb.prop = obja; // objb的引用次数和obja的引用次数再+1
    // obja 和 obj2 的引用次数均是2
    // 变量永远不会被清除,造成内存泄漏
}
4.3.3 性能优化

优化性能问题上,IE6 根据固定的内存分配量来触发 gc。但是如果脚本中声明了很多变量,并且都没有被释放,那么一直会达到触发标准,gc 会高频率触发,效率低下。

es7 做出了改进:临界值是动态计算的。如果一次垃圾回收的内存量低于 15%,那么临界值会翻倍;如果高于 85%,重置临界值。

4.3.4 管理内存

解除引用:不使用的变量,设置为null

解除引用不意味变量内存回收,而是让其脱离执行环境,方便下次 gc 回收。

第五章 引用类型

ECMAScript 是面向对象语言,但不是传统的面向对象。提供构造函数,专门对接传统对象编程。

5.1 Object 类型

new Object(){} 声明等效。

5.2 Array 类型

创建有Array[]2 种方式。

length 是可读写的,置 0 可以清空数组。

5.2.1 数组检测

请用 Array.isArray 检测数组。instanceof 不适用于网页包含多个框架,2 个运行环境,从一个向另一个传入数组构造函数,严格意义上并不相等。

代码语言:javascript
复制
<script>
    const { frames } = window;
    const length = frames.length;
    xArray = frames[length - 1].Array;
    const arr = new Array();
    console.log(arr instanceof xArray); // false
script>
5.2.3 栈和队列
  • 栈:push && pop
  • 队列:push && shift
5.2.6 操作方法

concat:参数会被自动展开

代码语言:javascript
复制
const colors = [1];
const colors2 = colors.concat(2, [3, 4]); // [1, 2, 3, 4]

slice(star, end): 切片,返回新数组。

splice(start, count, …items):

  • 删除:不需要第三个参数
  • 插入:第二参数置 0
  • 替换:第二个和第三个参数要用

5.3 Date 类型

Date.now() 和 new Date().gewNow() 等价。

Date.parse(string): 返回 string 代表的日期的毫秒数。年/月/日,请不要使用-连接!

Date 实例可以直接比较大小,因为valueOf返回毫秒数。

5.4 RegExp 类型

不推荐 new RegExp(string) 来声明正则,因为 string 是字符串,元字符需要双重转义。比如\n,就是\\n

每个实例拥有以下属性:

  • global:g
  • ignoreCase: i
  • multiline: m
  • lastIndex: 搜索下一匹配项的字符位置
  • source: 正则的字符串表示

5.5 Function 类型

代码求值时,js 引擎会将声明函数提升到源码顶部。

arguments上重要属性:

  • length:参数长度
  • callee: 函数自身引用

函数上重要属性:

  • caller: 调用此函数的函数引用。全局访问返回 null
  • length:函数希望接受的参数个数(不算默认参数)
代码语言:javascript
复制
function outer() {
    inner();
}
function inner(a, b = 1) {
    console.log(arguments.callee.caller === outer);
}

outer(); // true
inner.length; // 2 - 1 = 1

函数 prototype 属性无法枚举,不能用 for-in 枚举

  • 可以使用 Object.getOwnPropertyNames ,返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性但不包括 Symbol 值作为名称的属性)组成的数组。
  • 可以使用 Reflect.ownKeys,返回包括所有自身属性的属性名的数组

5.6 基本包装类型

num.toFixed(位数):自动舍入,返回字符串。

num.toExponential(位数):转化为科学计数法,返回字符串。

String.fromCharCode(…charcodes): 将字符编码转化为字符串。

String.charCodeAt(index): 将 index 的字符转化为字符编码。

5.7 单体内置对象

随机整数生成:

代码语言:javascript
复制
// [start, end]
function randomInt(start, end) {
    const times = end - start + 1;
    return Math.floor(Math.random() * times + start);
}

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

ECMA-62 对象定义:无序属性集合,其属性可以包括基本值、对象和函数。

6.1 理解对象

ECMA 有 2 种属性:数据属性和访问器属性。它们可以通过 Object.getOwnPropertyDescriptor 来读取。

1.数据属性

通过 Object.defineProperty(对象, 属性名, {属性: 值}) 来修改,可修改的属性是:configurable(是否可通过delete删除)、enumerable(能否 for-in 循环)、writable(能否修改)、value。

可以多次调用 api 修改上述属性,除了将 configurable 设置为 false。

2.访问器属性

访问器属性不包含数据值,也是通过 Object.defineProperty(对象, 属性名, {属性: 值}) 来修改。

可修改的属性是:configurable、enumerable、get、set。其中,只指定 get 不指定 set,那么就是不可写;反过来,不能读。

6.2 创建对象

6.2.1 理解原型对象

原型模式中,实例的 __proto__ 指向构造函数的 prototype,因此,构造函数.prototype.isPrototypeOf(实例)返回 true。

因为原型链有下端“屏蔽”上端的机制,可以通过逐步 delete 来暴露上端属性。

6.2.2 原型与 in 操作符

如果对象可以访问给定属性,那么 in 返回 true。

代码语言:javascript
复制
function Person() {}
Person.prototype.name = "student";
const person = new Person();
console.log("name" in person); // output

检测 prototype 是否位于 原型链上,而不位于实例上。

代码语言:javascript
复制
function hasPropertyInPrototype(object, prototype) {
    // hasOwnProperty 是否位于实例上
    return prototype in object && !object.hasOwnProperty(prototype);
}
6.2.3 自定义原型

(构造)函数的constructor 属性是自身,所以重写prototype的时候,需要注意:

代码语言:javascript
复制
function Person() {}
Person.prototype = {
    name: "dongyuanxin"
};
Object.defineProperty(Person.prototype, "constructor", {
    enumerable: false, // Person.prototype.constructor 是不可枚举的
    value: Person
});
6.2.4 动态原型

为了对应 OO 编程习惯,prototype 上属性在访问时动态创建:

代码语言:javascript
复制
function Person() {
    this.name = "person";

    if (typeof this.sayHello !== "function") {
        Person.prototype.sayHello = function() {
            console.log(`Hello, I'm ${this.name}`);
        };
    }
}
6.2.6 稳妥构造函数

经常使用,尤其是在对原生对象做拓展时候,而且不能影响原有原型链。

代码语言:javascript
复制
function PowerDate() {
    const date = new Date();
    date.format = () => {
        const year = addZeroStr(date.getFullYear()),
            month = addZeroStr(date.getMonth() + 1),
            day = addZeroStr(date.getDate()),
            hour = addZeroStr(date.getHours()),
            minute = addZeroStr(date.getMinutes()),
            second = addZeroStr(date.getSeconds());
        return `${year}/${month}/${day} ${hour}:${minute}:${second}`;
    };
    // 可以new调用,因为return重置了返回值
    return date;
}

6.3 继承

  • 接口继承:继承方法签名
  • 实现继承:继承实际方法

常见四种方法:JavaScript 基础知识梳理-下

第七章 函数表达式

7.2 闭包

闭包是指:有权访问另一个函数作用域中的变量的函数。作用域得到了延长。

一个经典问题:

代码语言:javascript
复制
function createFunction() {
    var result = new Array();

    for (var i = 0; i < 10; ++i) {
        result[i] = function() {
            return i;
        };
    }

    return result;
}

调用 result 中的函数,返回值均是 10。这是因为 var 不是块级作用域,闭包声明造成了内函数可以访问 createFunction 的作用域,并且在结束函数后,变量i的生命被延长了下来。例如,当调用 result[0] 的时候,就会访问并且返回 createFunction 中的 变量i的值。

如果将 var 换成 let,则不存在这个问题。虽然变量i生命被延长,也属于 createFunction作用域,但是let本身是“块级作用域”。也就是说,闭包中返回的i是当前循环下的i,没有发生污染。

7.3 模仿块级作用域

下面写法内存占用低,标记清除的gc在函数运行完,检测到不被使用,会立即销毁作用域链。

代码语言:javascript
复制
(function() {
    // ...
})();

7.4 私有变量

利用闭包,可以很巧妙地实现静态私有变量、私有函数方法等。

代码语言:javascript
复制
(function() {
    var name = ""; // 静态私有变量

    return {
        name() {
            return name + "123";
        }
    };
})();

第八章 BOM

8.1 window 对象

双重角色:js 访问浏览器的 api + ECMAScript 规定的 global 对象。

8.1.1 全局作用域

定义在全局的变量不能被 delete, 定义在 window 上的属性可以被 delete。

8.1.2 窗口关系及框架

对于 window 的frames,为了保证兼容性,请使用:top.frames。因为top是绝对的。

除了top外,还有parent,在没有任何框架情况下,top === window

最后,还有self。在 sw 中,常用 self 访问 window 上的 api。

8.1.3 窗口位置

跨浏览器取得窗口左边、上边的位置:

代码语言:javascript
复制
let leftPos =
    typeof window.screenLeft === "number" ? window.screenLeft : window.screenX;
let topPos =
    typeof window.screenTop === "number" ? window.screenTop : window.screenY;

此外,还有window.moveTo(x, y)window.moveBy(offsetX, offsetY)两个方法移动位置。但是默认是禁用的。

8.1.4 窗口大小

窗口大小无法确定,但是可以跨浏览器获得页面视图大小:

代码语言:javascript
复制
let pageWidth = window.innerWidth,
    pageHeight = window.innerHeight;

if (typeof pageWidth !== "number") {
    if (document.compatMode === "CSS1Compat") {
        // 是否是标准模式
        pageWidth = document.documentElement.clientWidth;
        pageHeight = document.documentElement.clientHeight;
    } else {
        // 是否是混杂模式
        pageWidth = document.body.clientWidth;
        pageHeight = document.body.clientHeight;
    }
}

此外,还有window.resizeTo(width, height)window.resize(offsetWidth, offsetHeight)调整大小。但是默认是禁用的。

8.1.5 导航和打开窗口

window.open(href, windowName, paramsString): 最后一个参数形如 height=400,width=10

这里有同域限制,并且返回的指针指向新开窗口,可以使用以上被禁用的方法。

对于一些浏览器插件,会禁用弹出,兼容代码如下:

代码语言:javascript
复制
let blocked = false;
try {
    let wroxWin = window.open("http://baidu.com", "_blank");
    if (!wroxWin) {
        // 打开失败
        blocked = true;
    }
} catch (error) {
    // 插件禁止后,会报错
    blocked = true;
}
8.1.7 系统对话框

它们是浏览器决定的,是同步和模态的。显示的时候,会终止代码执行。

8.2 location 对象

location.href(最常用) 和 window.location 本质都是调用 location.assign()。

除此之外,修改 location 上的其他属性,也可以改变当前加载的页面,比如 location.hash='#setion'

以上方法,会在浏览器中生成新的历史记录。使用location.replace()方法,不会在浏览器中生成历史记录。

location.reload(true):强制重新加载。

8.3 navigator 对象

8.3.1 检测插件

navigator.plugins 存放插件信息:

代码语言:javascript
复制
// 通用检测方法
function hasPlugin(name = "") {
    name = name.toLocaleLowerCase();
    for (var i = 0; i < navigator.plugins.length; ++i) {
        if (navigator.plugins[i].name.toLocaleLowerCase().indexOf(name) > -1) {
            return true;
        }
    }
    return false;
}

但由于 IE 浏览器的兼容,最好针对不同浏览器封装不同的插件检测方法。

8.3.2 注册处理程序

google 支持 register​Protocol​Handler 自定义协议。比如打开https://www.baidu.com的控制台,在其中输入:

代码语言:javascript
复制
// 理论上是这样,但是效果不好
navigator.registerProtocolHandler(
    "web+baidu",
    "https://www.baidu.com/s?wd=%s",
    "Baidu handler"
);

8.5 history 对象

history.go(): 任意跳转。数字代表前后跳转,字符串会自动找寻历史中最近的位置跳转。

history.length: 保存历史记录的数量。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-05-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 第一章 JavaScript 简介
    • 1.2 js 实现
    • 第二章 在 HTML 中使用 JavaScript
      • 2.1 script标签
        • 2.2 可扩展超文本标记语言 XHTML
          • 2.4 noscript 元素
          • 第三章 基本概念
            • 3.1 语法
              • 3.2 关键字和保留字
                • 3.3 变量
                  • 3.4 类型
                    • 3.4.1 null 和 undefined
                    • 3.4.2 8 和 16 进制
                    • 3.4.3 特殊数字
                    • 3.4.4 字符串转数字
                    • 3.4.5 字符串
                    • 3.4.6 Object 类型
                  • 3.5 操作符
                    • 3.6 语句
                      • 3.6.5 for-in 语句
                      • 3.6.6 label 语句
                      • 3.6.8 with 语句
                    • 3.7 函数
                    • 第四章 变量、作用域和内存问题
                      • 4.1 基本类型和引用类型的值
                        • 4.1.2 赋值
                        • 4.1.4 检测类型
                      • 4.2 执行环境和作用域
                        • 4.3 垃圾回收(GC)
                          • 4.3.1 标记清除和引用计数
                          • 4.3.3 性能优化
                          • 4.3.4 管理内存
                      • 第五章 引用类型
                        • 5.1 Object 类型
                          • 5.2 Array 类型
                            • 5.2.1 数组检测
                            • 5.2.3 栈和队列
                            • 5.2.6 操作方法
                          • 5.3 Date 类型
                            • 5.4 RegExp 类型
                              • 5.5 Function 类型
                                • 5.6 基本包装类型
                                  • 5.7 单体内置对象
                                  • 第六章 面向对象的程序设计
                                    • 6.1 理解对象
                                      • 6.2 创建对象
                                        • 6.2.1 理解原型对象
                                        • 6.2.2 原型与 in 操作符
                                        • 6.2.3 自定义原型
                                        • 6.2.4 动态原型
                                        • 6.2.6 稳妥构造函数
                                      • 6.3 继承
                                      • 第七章 函数表达式
                                        • 7.2 闭包
                                          • 7.3 模仿块级作用域
                                            • 7.4 私有变量
                                            • 第八章 BOM
                                              • 8.1 window 对象
                                                • 8.1.1 全局作用域
                                                • 8.1.2 窗口关系及框架
                                                • 8.1.3 窗口位置
                                                • 8.1.4 窗口大小
                                                • 8.1.5 导航和打开窗口
                                                • 8.1.7 系统对话框
                                              • 8.2 location 对象
                                                • 8.3 navigator 对象
                                                  • 8.3.1 检测插件
                                                  • 8.3.2 注册处理程序
                                                • 8.5 history 对象
                                                领券
                                                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档