# javascript
# javascript 数据类型
# 类型分类
基本 (值) 类型: String, Number, Boolean, null, undefined
对象 (引用) 类型: Object, Function, Array
, 其中 Function 和 Array 也是一种特别的 Object 类型
# 类型判断
typeof , 返回对象的字符串表示形式,由下表可以看出 typeof
可以用来比较 String, Number, Boolean, undefined, Function
等类型,但是无法比较 null, Array, Object
, 因为 typeof(null), typeof(Array), typeof(Object)
返回的都是 'object'
Type | Result |
---|---|
String | ‘string’ |
Number | ‘number’ |
Boolean | ‘boolean’ |
null | ’object’ |
undefined | ‘undefined’ |
Object | ’object’ |
Array | ’object’ |
Function | ‘function’ |
instanceof 用于判断对象的具体类型,可以判断 Object, Array, Function
, 由于 Function 和 Array 也是一种特别的 Object 类型,所以 Array|Function's instance instanceof Object 返回 true
, 具体判断如下
Instance | Type | Result |
---|---|---|
array | Array | true |
function | Function | true |
array | Object | true |
function | Object | true |
object | Object | true |
=== 可以用来判断 undefined, null
因为它们是全局唯一的,undefined 和 null 区别如下:
undefined 表示变量未赋值,null 表示变量赋了值,但是值为 null
null 作用:
- var b = null 初始赋值为 null, 表明将要赋值为对象(typeof b === ‘object’)
b = [‘a’, 2] - b = null 让 b 指向的对象成为垃圾对象(被垃圾回收器回收)
# 数据类型和变量类型
数据类型 | 变量类型(变量内存值的类型) |
---|---|
基本类型 | 基本类型:保存的是基本类型的数据 |
引用类型 | 引用类型:保存的是地址值 |
# 数据、变量和内存
内存是存储数据的空间,变量是内存的标识
数据
存储在内存中代表特定信息的东西,本质上是 ‘0101…’, 数据是内存中所有操作的目标,算术运算、逻辑运算、赋值、运行函数
数据特点:可传递,可运算
1 | var a = 2; |
内存
内存条通电后产生的可一存储数据的空间,是临时的,断电后内存空间和数据都会消失,一块小内存包含 内部存储的数据
和 地址值
内存分为栈和堆
栈:存储全局变量和局部变量
1 | var a = 1; |
堆:存储对象
变量
可变化的量,由变量名和变量值组成,每个变量都对应一块内存,变量名用来查找对应的内存,变量的值就是内存中保存的数据
# 引用变量赋值
1 | var obj1 = { name: "aaa" }; |
# javascript 函数
# 函数定义方式
函数声明和函数表达式
1 | // 函数声明 |
# 函数调用
call | desc |
---|---|
test() | 直接调用 |
new test() | new 方式调用 |
obj.test() | 对象调用 |
test.call/apply(obj) | 让 test 成为 obj 的方法调用 |
# 回调函数
定义的函数没有手动调用,但最终它会执行,常见回调函数
- dom 事件回调函数
- 定时器函数
- ajax 请求回调函数
- 生命周期回调函数
# IIFE
Immediately-InvokedFunctionExpression (立即执行函数表达式),其实就是 匿名函数自调用
,作用是隐藏实现,并且不会污染全局空间
1 | (function () { |
# this
任何函数本质上都是通过某个对象调用,在浏览器中没有直接指定就是 window,所有函数内部都有一个变量 this, 它的值是调用函数的当前对象,可通过如下方式判断:
call | this |
---|---|
test() | window |
new test() | 新创建的对象 |
obj.test() | obj |
test.call/apply(obj) | obj |
1 | function Person(color) { |
# 何时加分号
需要加分号 ;
的地方,以 () or []
开头的语句
1 | // 如果不加分号就会出现 3(...), TypeError: 3 is not a function |
# js 调用函数传递变量参数是值传递还是引用传递
可以说都是值(基本 / 地址值)传递
也可以说可能是值传递,也可能是引用传递 (地址值)
# javascript 对象
-
什么是对象
用来保存多个数据的容器,用于统一管理数据
一个对象用来代表现实世界中的一个事物 -
对象组成
属性
方法(一种特别属性) -
访问对象内部数据
. 属性名
[属性名] - 属性名包含特殊字符 -、空格、变量名等时使用
1 | var p = {}; |
# javascript 原型
原型对象
-
每个函数都有一个 prototype 属性,它默认指向一个 Object 空对象(即原型对象)
-
原型对象中有一个属性 constructor, 它指向函数本身
作用
- 函数对象的所有实例会自动拥有原型中的属性和方法
显示原型和隐式原型
-
每个函数 function 都有一个 prototype, 即显示原型属性,默认指向一个空的 Object 对象
-
每个实例对象都有一个
__proto__
, 即隐式原型 -
对象隐式原型的值为其构造函数的显示原型的值
原型链
- 访问一个对象的属性时,先从自身查找,然后沿着
__proto__
向上查找, - 当查找不到时 (
__proto__
=== null) 返回 undefine
总结
- 函数的 prototype 属性在定义函数时自动添加,默认为 Object 空对象
- 对象的
__proto__
属性在创建对象时自动添加,默认为构造函数的 prototype 属性值
-
所有函数都是通过 new Function () 产生的,所以所有函数的
__proto__
都相等,都是 Function.prototype -
因为 Function 也是通过 new Function () 产生的,所以 Function.
__proto__
=== Function.prototype
1 | function Fn() {} |
图解原型链
# node
# 模块
# exports
exports 对象是当前模块的导出对象,用于导出模块公有方法和属性。别的模块通过 require 函数使用当前模块时得到的就是当前模块的 exports 对象。以下例子中导出了一个公有方法。 const exports = module.exports
, exports 是 module.exports 的快捷方式,NodeJS 对外暴露模块是通过 module.exports, 不能修改 exports 的指向,否则就不会导出模块。
1 | exports.hello = function () { |
# module
通过 module 对象可以访问到当前模块的一些相关信息,但最多的用途是替换当前模块的导出对象。例如模块导出对象默认是一个普通对象,如果想改成一个函数的话,可以使用以下方式。
1 | module.exports = function () { |
以上代码中,模块默认导出对象被替换为一个函数。
# 模块初始化
一个模块中的 JS 代码仅在模块第一次被使用时执行一次,并在执行过程中初始化模块的导出对象。之后,缓存起来的导出对象被重复利用。例如有以下目录。
1 | -/home/ersu / hello / -util / counter.js; |
其中 counter.js 内容如下:
1 | var i = 0; |
该模块内部定义了一个私有变量 i,并在 exports 对象导出了一个公有方法 count。主模块 main.js 内容如下:
1 | var counter1 = require("./util/counter"); |
运行该程序的结果如下:
1 | $ node main.js |
可以看到,counter.js 并没有因为被 require 了两次而初始化两次。
# 模块路径解析规则
内置模块
如果传递给 require 函数的是 NodeJS 内置模块名称,不做路径解析,直接返回内部模块的导出对象,例如 require (‘fs’)。
node_modules 目录
NodeJS 定义了一个特殊的 node_modules 目录用于存放模块。例如某个模块的绝对路径是 /home/user/hello.js,在该模块中使用 require (‘foo/bar’) 方式加载模块时,则 NodeJS 依次尝试使用以下路径。
/home/user/node_modules/foo/bar
-> /home/node_modules/foo/bar
-> /node_modules/foo/bar
NODE_PATH 环境变量
与 PATH 环境变量类似,NodeJS 允许通过 NODE_PATH
环境变量来指定额外的模块搜索路径。NODE_PATH 环境变量中包含一到多个目录路径,路径之间在 Linux 下使用 :
分隔,在 Windows 下使用 ;
分隔。例如定义了以下 NODE_PATH 环境变量:
NODE_PATH=/home/user/lib;/home/lib
当使用 require (‘foo/bar’) 的方式加载模块时,则 NodeJS 依次尝试以下路径。
/home/user/lib/foo/bar
-> /home/lib/foo/bar
# 命令行程序
在 Windows 系统下的做法完全不同,我们得靠.cmd 文件来解决问题。
假设 node-echo.js 存放在 C:\Users\user\bin 目录,并且该目录已经添加到 PATH 环境变量里了。
接下来需要在该目录下新建一个名为 node-echo.cmd 的文件,文件内容如下:
@node "C:\User\user\bin\node-echo.js" %*
这样处理后,我们就可以在任何目录下使用 node-echo 命令了。
# package.json 中的 bin
许多包有一个或多个可执行文件希望被安装到系统路径。在 npm 下要这么做非常容易 (事实上,npm 就是这么运行的)。这需要在你的 package.json 中提供一个 bin 字段,它是一个命令名和本地文件名的映射。在安装时,如果是全局安装,npm 将会使用符号链接把这些文件链接到 prefix/bin,如果是本地安装,会链接到./node_modules/.bin/。比如,要使用 myapp 作为命令时可以这么做:
1 | { "bin": { "myapp": "./cli.js" } } |
这么一来,当你安装 myapp,npm 会从 cli.js 文件创建一个到 /usr/local/bin/myapp 的符号链接 (这使你可以直接在命令行执行 myapp)。如果你只有一个可执行文件,那么它的名字应该和包名相同,此时只需要提供这个文件路径 (字符串),比如:
1 | { |
这和以下这种写法相同:
1 | { |
# process
process 是一个全局变量,可通过 process.argv 获得命令行参数。由于 argv [0] 固定等于 NodeJS 执行程序的绝对路径,argv [1] 固定等于主模块的绝对路径,因此第一个命令行参数从 argv [2] 这个位置开始。
1 | var fs = require("fs"); |
# 文件操作
# Stream
stream 以流的方式读写文件
1 | var fs = require("fs"); |
# BOM 的移除
BOM 用于标记一个文本文件使用 Unicode 编码,其本身是一个 Unicode 字符("\uFEFF"),位于文本文件头部。
在不同的 Unicode 编码下,BOM 字符对应的二进制字节如下:
1 |
|
因此,我们可以根据文本文件头几个字节等于啥来判断文件是否包含 BOM,以及使用哪种 Unicode 编码。但是,BOM 字符虽然起到了标记文件编码的作用,其本身却不属于文件内容的一部分,如果读取文本文件时不去掉 BOM,在某些使用场景下就会有问题。例如我们把几个 JS 文件合并成一个文件后,如果文件中间含有 BOM 字符,就会导致浏览器 JS 语法错误。因此,使用 NodeJS 读取文本文件时,一般需要去掉 BOM。例如,以下代码实现了识别和去除 UTF8 BOM 的功能。
1 | function readText(pathname) { |
# GBK 转 UTF8
NodeJS 支持在读取文本文件时,或者在 Buffer 转换为字符串时指定文本编码,但遗憾的是,GBK 编码不在 NodeJS 自身支持范围内。因此,一般我们借助 iconv-lite 这个三方包来转换编码。使用 NPM 下载该包后,我们可以按下边方式编写一个读取 GBK 文本文件的函数。
1 | var iconv = require("iconv-lite"); |
# http
1 | // server |
# event
扩展事件发射器实现文件监听
1 | var events = require("events"); |
# 正则
\b
匹配单词边界
x(?=y)
如果 y 跟随在 x 后面,匹配 x
x(?=y)
如果 y 不跟随在 x 后面,匹配 x
1 | let date = new Date("2019-03-01T08:02:06Z"); |
# 判断空字符串,null,undefined
1 | // !name 可以判断 name="", null, undefined |
# GenerateRandomAlphaNum
1 | // toString(36) 转换为 36 进制 |
# parseInt & ~~
1 | console.log(~1.5, ~-1.5); // -2 0 |
# const
1 | // es5 |
# node .
1 | { |
Node will try to load module located in the folder you pass (. - just bash variant of current folder), and start whatever is written in “main” section of package.json.
if no package.json found in the folder, node will still try to run index.js file.
# JS trap
# 1 / 0
在 node 中除以 0 不会产生异常
1 | console.log(0 / 0, 1 / 0, -1 / 0); |
# undefined compare
undefined 可以和其它类型比较
1 | console.log(undefined < 1, undefined > 1, undefined < "1"); |
# JS Quiz
# 数组扁平化,去重,升序
1 | const arr = [ |
# 实现 AOP
1 | Function.prototype.before = function (callback) { |