ECMAScript笔记(八)零散知识
解决命名冲突
前提
在实际开发中,往往是由很多工程师共同完成一个项目。工程师各自负责不同的功能开发,最后整合代码到一起。
整合代码时,就有可能出现各种各样的冲突。比如 HTML 的冲突,CSS 的冲突,还有 JavaScript 功能的冲突。
其中,JavaScript 不同功能的代码块中各自定义的变量,由于整合到一起,所以会出现互相冲突或直接覆盖的问题。
为了避免变量命名冲突,出现了以下几种方法。
方法一:命名空间 Namespace
为项目或库创建一个全局对象,然后将所有功能添加到该全局变量中,减少程序中全局变量的数量,实现单全局变量。这样避免了在具有大量函数、对象和其他变量的情况下造成全局污染,也解决了命名冲突等问题。
定义一个全局对象,它在主域的 JavaScript 中。
1 | var org = {} |
全局对象里又包含多个对象。这些对象中存储每个功能的变量,这样就能区分开各功能模块的变量。
1 | var org = { |
如果要使用这些变量:
1 | var tuan = org.department1.tuan; // 简化引用值名称 |
这是个比较早期采用的方法,现在不再使用了。
方法二:闭包
运用闭包和立即执行函数。
把特定的功能写进去,然后留出一个接口,方便后续调用。
这个接口就是 return 出去的函数,这个函数里放入功能函数的调用。
功能函数会使用立即执行函数里的私有化变量。
return 的函数作为一个中转站存在。
好处:不污染全局变量,也不影响各自的功能实现。
例如:
1 | var name = 'origin'; // 全局变量 |
方法三:webpack
链式调用(模仿 jQuery)
jQuery 链式调用:
1 | $('div').css('background-color', 'red').width(100).height(100).html(123).css('position', 'absolute').css('left', '100px').css('top', '100px'); |
对象内的方法调用:
- 函数内没有
return语句时,默认会返回undefined值。 tuan.eat()调用完成后,如果接着调用.drink(),就会报错:“没有在 undefined 上找到 drink 方法。”
1 | var tuan = { |
- 在一个对象的函数(方法)里,
this指向的是这个对象。 - 所以,我们之间在方法(函数)里增加语句
return this;。
1 | var tuan = { |
访问属性
成员操作符['属性名']或者.操作符可以访问到对象的属性,还可以给对象的属性赋值。
两者作用基本等同,每当你使用.操作符访问属性时,系统内部会自动转换成[]操作符。
但是以下情况需要使用[]:
- 属性名比较特殊,比如包含空格或者连字符。
- 需要用变量做属性名。
1 | var user = { |
- 属性名拼接。因为
[]操作符内填写的是字符串形式的属性名,所以可以通过+进行拼接。
1 | var tuan = { |
对象枚举
对象枚举(enumeration),又叫遍历,就是将对象的每个属性(属性名+属性值)挨个访问一遍。for循环语句可以遍历数组。
for…in 循环语句
对象的遍历则需要使用for...in循环语句。它的功能只有一个,就是遍历对象。
- 「键名」可以是任意合法的标识符,它在循环的过程中会逐个被赋值为「对象」的属性名。
对象[键名]为对象的属性值。
1 | for (var 键名 in 对象) { |
特点:通过对象属性的个数来控制循环圈数。
数组算特殊类型的对象,所以也可以使用for...in语句。
1 | var arr = ['a', 'b', 'c']; |
输出属性名:
1 | var tuan = { |
输出属性值:(注意[]和.操作符的差异)
tuan.key-> 系统内部自动转换成tuan['key'],导致的结果:系统是把 key 当做 tuan 的一个属性,因为它没有这个属性,所以会输出 undefined。
1 | for (var key in tuan) { |
- 所以需要使用
[]操作符,这样tuan[key]中的key才是变量:
1 | for (var key in tuan) { |
for...in语句在遍历时,会将对象原型上的属性也访问一遍。不过,它不会遍历到原型链终端Object.prototype系统自带的属性。如果手动给它增加属性,是可以遍历到的。
1 | var tuan = { |
hasOwnProperty() 方法
任何对象都有hasOwnProperty()方法,它可以判断属性是否属于这个对象。括号内传入要判断的属性名(字符串形式)。如果是,它会返回布尔值true。
1 | var tuan = { |
增加到for...in语句中,可以过滤掉原型上的属性,只留下自己的。
1 | var tuan = { |
判断是不是自己的属性,不包括原型链上的。
in 操作符
in 操作符可以判断这个对象能不能访问到这个属性 ,包括原型上的。操作符前面要用属性名(字符串形式)。
1 | var tuan = { |
但是 in 操作符有个问题,它不能判断这个属性是属于对象自己的,还是原型上的。
1 | var tuan = { |
判断某个属性能否访问到,包括原型链上的。
instanceof 操作符
格式:A instanceof B。判断对象 A 是不是构造函数 B 生产出来的。
判断方法:看对象 A 的原型链上有没有构造函数 B 的原型。
1 | // 对象的原型链终端:Object.prototype |
判断某个对象是否是这个构造函数生产出来的。
区分数组和对象
如果有一个数据,需要用方法去判别它是对象还是数组。
1 | var arr = [] | {}; |
构造器 constructor
如下:
1 | console.log([].constructor); // [Function: Array] |
instanceof 操作符
如下:
1 | console.log([] instanceof Array); // true |
toString() 方法
toString()是定义在Object.prototype上的实例方法,所有实例对象继承了该方法,其可以返回一个对象的字符串形式。
1 | {}.toString(); // [object Object] |
对于空对象和一般对象,toString()方法会继承于Object.prototype.toString(),默认返回[object Object],其中第二个 Object 是该对象的构造函数,那么根据这个值可以判断数据类型。
然而,字符串、数组、Date 等对象拥有自定义的toString()方法,会覆盖Object.prototype.toString()方法。
1 | 'abc'.toString(); // "abc" |
可以直接使用Object.prototype.toString()方法来获得类型,但由于是在 Object 对象环境中使用方法,所有对象都会显示[object Object]。
配合call(),改变toString()方法执行时的所在的环境,可以得到对象的精确类型。
Object.prototype里的toString()方法,正常情况下,这个方法被谁调用,那么它里面的 this 就是谁。toString()方法是为了处理前面的调用者,对象调用了它,那么这个 this 就是对象。它将前面的参数识别出来,然后返回字符串形式。
1 | Object.prototype.toString = function () { |
- 原来是谁调用,this 就是谁。现在直接用
call()方法把 this 指向改掉。
1 | console.log(Object.prototype.toString.call([])); // [object Array] |
this
函数预编译过程中,除了之前说过的形参、变量和函数声明,还包括arguments和this。
1 | function test(target) { |
这里的this指向 window(即 GO,全局)。
1 | function test() { |
全局作用域里的this也指向 window。
1 | console.log(this); // Window {......} |
call()和apply()方法可以改变函数运行时this的指向。
在对象里,谁调用了方法,这个方法里面的 this 就是谁。
- 对象的方法执行时同样会走预编译,预编译完成后,this 会根据调用者来改变指向。
- 对象 tuan 调用了 test 方法,那么它里面的 this 就指向 tuan 这个对象。
1 | var tuan = { |
- 如果没人调用它,直接空执行,那么
this就是指向 window 的。
注意,如果在对象方法里再执行一个函数,这个函数里面的 this 没人调用的话,那么它同样是指向 window 的。
- 对象 b 调用 say 方法,里面的 this 指向 b
- 把 a.say 的函数引用当作实际参数传入 b 的 say 方法中
- 相当于
fun = function () {console.log(this.name)}; - 函数 fun 在 b 的 say 方法里执行,没有人调用它
- 所以按照预编译,函数 fun 里的 this 指向 window
1 | var name = '2'; |
arguments
arguments是一个对应于传递给函数的参数的类数组对象。
属性有:arguments.callee、arguments.length。
arguments.callee指向当前执行的函数。
1 | function test () { |
用处:比如需要初始化工具时,立即执行函数没有函数名,而在函数内又需要用到它自身,就可以用来arguments.callee来代替。
1 | var num = (function(n) { |
arguments.length指向传递给当前函数的参数数量。
function.caller返回调用函数。
- 如果一个函数
f是在全局作用域内被调用的,则f.caller为null。
1 | function piao () { |
- 如果一个函数是在另外一个函数作用域内被调用的,则
f.caller指向调用它的那个函数。
1 | // demo 在 test 函数作用域内被调用 |
克隆
浅层克隆
浅克隆的对象的引用值是==拷贝==对象里的引用,这两个对象里面的引用(如对象里的数组或者内嵌对象)指向的地方是一致的。
原始值是值拷贝,各自改各自的,没有影响。
例如:
- 1# 处声明一个 target 变量,是为了保证用户在没有传入第二个变量时,程序依然可以正常运行。
- 因为对象
cat2的引用值是直接拷贝cat的引用,它俩指向同一个地址,所以一个改动,另一个也会改动。
1 | var cat = { |
深层克隆
两个对象里的引用独立拷贝,不指向同一个地方。
步骤如下:
遍历每个属性。
for(var prop in obj){}- 遍历时需注意,
for...in会将对象的原型链上的属性也拿出来,所以需要hasOwnProperty()方法验证该属性是否属于这个对象。
判断属性是不是原始值,原始值直接进行值拷贝。
typeof()null的数据类型也被识别为object,所以需要把它去掉。
如果是引用值,判断引用值是数组还是对象。
toString()、instanceof、constructor考虑到父子域跨域访问的问题,建议使用
toString()
建立相应的空引用值(数组或对象)。
{}、[]
把要拷贝的引用值当作一个新的对象或数组,然后从第一步开始循环。
- 递归
规律:判断引用值里的属性是原始值还是引用值……
- 出口:当这个属性是原始值时
else {target[prop] = origin[prop]}
1 | var tuan = { |
三目运算符
又叫条件运算符、三元运算符。它是 JavaScript 中唯一需要三个操作数的运算符。运算的结果根据给定条件在两个值中取其一。语法为:
1 | 条件 ? 值1 : 值2 |
如果条件为真,则结果取值1。否则为值2。你能够在任何允许使用标准运算符的地方使用条件运算符。
功能类似if...else语句。它还自带return返回值。
例:
- 有括号先看括号
- 字符串之间的比较是比较 asc 码,字符串’10’(一零)和字符串’9’逐位比较,
'10' < '9'
1 | var num = 4 > 3 ? ('10' > '9' ? 1 : 0) : 2; |
可以使用三目运算符简化深层克隆里的部分步骤。
1 | function deepClone(origin, target) { |
字符串 String
字符串原始值无法拥有属性和方法,但是字符串对象可以有。
length属性:返回字符串的长度。
indexOf('文本', 检索起始位置)方法:返回字符串中指定文本首次出现的位置(索引)。未找到会返回 -1。
lastIndexOf():返回指定文本最后一次出现的位置。未找到会返回 -1。注意:该方法向后进行检索(从尾到头),这意味着:假如第二个参数是 50,则从位置 50 开始检索,直到字符串的起点。
search():搜索特定值的字符串,并返回匹配的位置。
区别:
- search() 方法无法设置第二个开始位置参数。
- indexOf() 方法无法设置更强大的搜索值(正则表达式)。
例如:
1 | var str = 'hhh略略略啦啦啦xfsdsdfcdeewruiggvsfasdfghj'; |
slice(从该位开始截取,截取到该位之前):截取字符串的某部分并返回该部分。
补充
任何使用 var 声明的属性不能从全局作用域或函数的作用域中删除。
通过 var 操作给 window 上增加的属性,这种属性叫做不可配置的属性。不可配置的属性无法进行 delete 操作。
1 | var num = 123; |
引用值没必要研究隐式类型转换,如果一定要转换也是可以的。
1 | // 空数组不等于空数组,引用值地址不一样 |







