@15013890200
2018-08-31T18:01:48.000000Z
字数 4534
阅读 531
原生javascript
js庞大的技术框架体系,使得网页编程变得越来越便捷。然而前端技术更新速度确是十分快速的,两年时间就有可能横空出世一套新的框架体系而代替我们正在使用的框架。因此,javascript基础就显得尤为重要。无论哪一套框架都是基于原生js开发的,都能够无缝兼容原生js。理论上我们可以用原生js实现框架提供的一切功能。
距上一次拜读《JavaScript高级程序设计(第三版)》这本书已经过去将近一年了,有必要重读一遍,希望可以有新的收获。
两种:基本类型值、引用类型值
主要区别:基本类型值的变量占内存空间固定,变量复制时会创建这个值的副本。
引用类型值的变量占内存空间不固定,其实质是一个指向该对象的指针,变量复制其实是指针复制,最终指向同一变量。
- 最常用的回收机制就是
标记清除
。当变量进入环境时,就将变量标记为“进入环境”。当变量离开时,标记为“离开环境”。- 局部变量会在离开执行环境时自动被解除引用。全局变量会一直占有内存空间,当全局变量不再有用时,可以将变量值置null来释放其引用。
- 解除一个值的引用并不意味着系统会自动回收该值占用的内存。其意义是让值脱离执行环境,以便垃圾收集器下次运行时将其收回。
- javascript自身的垃圾回收机制,不需要开卡人员过多关心垃圾回收问题,不过也可以主动触发垃圾收集函数。
两种:点表示法和方括号表示法
一般都是对象点属性名访问,也可以通过对象方括号属性名来访问。方括号的主要优点是可以通过变量来访问属性。(用于无法直接知晓属性名情况)
push(末尾添加)、pop(末尾移除)、shift(前端移除)、unshift(前端添加)、reverse(反序)、sort(排序,字符串)、concat(基于当前数组创建新数组)、slice(截取数组)、splice(改变数组)、indexOf、lastIndexOf
- push+pop模拟栈行为;push+shift模拟队列行为;unshift+push模拟反向队列行为
- concat可以传入多个参数,slice可以传入1个或两个参数,当传入两个参数代表返回起始和结束位置之间的项,但不包括结束位置。且slice不会影响原数组。
- splice功能强大,可以巧妙用于删除、插入、替换数组项。splice(开始位置,影响项数,要插入的项1,要插入的项2,...),当第二个参数为0,代表插入。当第二个参数值等于后面项数个数代表替换。splice会影响原数组。
数组迭代方法:every、filter、forEach、map、some;均不改变原数组
- every(): 对数组中的每一项允许给定函数,每一项都返回true,则返回true
- some(): 对数组中的每一项允许给定函数,任一项返回true,则返回true
- filter(): 对数组中的每一项允许给定函数,函数会返回true的项组成的数组
- forEach(): 对数组中的每一项允许给定函数,这个方法没有返回值(和for循环功能一致)
- map(): 对数组中的每一项允许给定函数,返回每次函数调用的结果组成的数组
reduce 和 reduceRight
- apply和call作用没什么两样,只是在使用的时候传参方式不同。二者第一个参数都是this,apply第二个参数是数组或者arguments,call之后的参数是详细列出的每一个参数
- 扩充函数作用域的好处是,函数和对象不需要有任何耦合关系。
- bind作用也是延长函数作用域,该方法会创建一个函数实例,其this值会被绑定到传给bind()函数的值。
Number类型:toString、toFixed、toPrecision、toExponential
String类型:charAt、charCodeAt、concat、slice、substr、substring、indexOf、lastIndexOf、trim、toLowerCase、toUpperCase、exec、match、search、replace、split、localeCompare
不属于其他任何对象的方法和属性的,实际上都是Global对象的方法和属性。其特性是可以直接拿来使用,无需声明。也可以通过window对象访问。另外,声明在全局的变量和方法也都是window的属性
方法如:isNaN、parseInt、parseFloat、encodeURI、encodeURIComponent、eval
属性如:undefined、NaN、Infinity
- encodeURI、encodeURIComponent对URI进行编码,前者对整个链接进行编码,且只编码空格字符。后者对所有非字母数字字符进行编码。所以后者只能对附加在URI后面的字符串使用。对应的,decodeURI、decodeURLComponent解码相应的部分。它们是之前
escape和unescape
方法的代替。- eval将传入的参数解析成标准的javascript语句。耗性能,不推荐使用。
- 可以通过Object.defineProperty、Object.defineProperties方法配置对象的属性,主要配置:configurable、enumable、writable、value。(实际开发过程中基本上用不到这高级功能,下同)
- 访问器属性getter、setter
- 构造函数
- 对象字面量方法(创建单个对象首选)
- 工厂模式(创建多个对象)
- 构造函数模式
- 原型模式
- 构造函数模式+原型模式(最推荐!!!)
- 寄生构造函数模式
工厂模式和构造函数模式区别:
* 工厂模式函数内部显示的声明了对象,以及返回该对象。构造函数模式没有
* 构造函数模式直接将构造函数的作用域赋给新对象任何函数,只要通过new操作符来调用,那它就可以作为构造函数;而任何函数,如果不通过new操作符来调用,那它跟普通函数没啥区别。
寄生构造函数,实质上是工厂模式和构造函数结合。工厂模式书写构造函数,构造函数模式(new)实例化对象。
- 原型属性伴随着函数而产生,实质是个指针,指向一个对象。用于定义所有实例共享的属性和方法。优点:不必在构造函数中定义对象实例的信息,而是将这些信息直接添加到原型对象中。
所有实例都无法访问到prototype属性,但可以通过isPrototypeOf方法来确定对象之间是否存在这种关系
- 对象实例可以访问保存在原型中的值,但却修改不了。如果在对象实例中创建一个同名属性,则该属性将会屏蔽原型中的那个属性。
- 借助in和hasOwnProperty可以判断属性存在对象实例中还是对象原型中。
- 当借助对象字面量语法设置原型属性时,原型的constructor不再指向构造函数本身。如果需要,则在原型中添加constructor属性指向构造函数。
- 原型的动态性(不太重要)
原型的缺点:
原型中属性值如果是是基本类型,是不会有任何问题。但是如果属性值是引用类型的时候,问题很严重。某个实例对象如果修改了这个引用类型值的时候,其他实例对象对应的改属性值均会改变。(当然这也很可能是原型的有点,如果场景需要所有实例对象该属性均需保持一致)。
- 原型链继承:1、鉴于原型链是继承是基于将父构造函数实例化对象传为原型对象。所以实例化对象所包含的属性均会成为子原型链的属性,对于引用类型值来说是会共享数据的。2、在创建子类型的实例时,不能向超类型的构造函数中传递参数。(因此很少会单独使用原型链)
function Super1() {
this.color = ['red','blue'];
}
function Sub1() {
}
Sub1.prototype = new Super1();
let super1 = new Super1();
super1.color.push('yellow');
console.log(super1.color); //['red','blue','yellow']
let super11 = new Super1();
console.log(super11.color); //['red','blue']
let sub1 = new Sub1();
console.log(sub1.color); //['red','blue']
sub1.color.push('black');
let sub11 = new Sub1();
console.log(sub11.color); //['red','blue','black']
- 借用构造函数(也较少单独使用)
function Super1() {
this.color = ['red','blue'];
}
function Sub1() {
Super1.call(this); // 这样就避免了原型链继承引用类型共享参数值的问题
}
组合继承,组合原型链和借用构造函数
组合式继承,实际上是借助apply/call在实例对象的属性上复制了父构造函数的所有属性。实例对象的原型依然保有父构造函数的所有属性。不过依据原型的特色,实例化同名属性会屏蔽原型中的同名属性。
原型式继承
借助
Object.create(obj)
方法可以让一个对象与另一个对象保持类似,一样引用类型值始终是共享的。- 寄生式继承(类似与工厂模式)
- 寄生组合式继承(为了解决组合继承调用两次超类型构造函数的问题)
1、直接在递归函数体内直接显示的调用函数名。缺点:当将函数赋值给其他变量名,则调用是会报错。
2、借助arguments.callee调用函数。缺点:在严格模式下不兼容
3、写法和1类似,不过将方法1整体赋值给另一个变量,再调用(推荐使用!!!)
var calc2 = (function f(num1,num2){
if(num1 >= num2){
return num2;
}
else {
return num1+f(num1+1,num2);
}
});
console.log(calc2(1,55));
- 是指有权访问另一个函数作用域中的变量的函数。匿名函数内部可以访问全局变量以及父函数的局部变量。即使父函数的作用域被销毁,父函数所包含的局部变量对象也还是存在内存中,直到匿名函数的作用域被销毁才会释放。
- 闭包的this对象通常指向window。
- 用途:1、能够在函数外部访问函数内部变量。2、让函数内部变量始终保持在内存中
- 不足:1、滥用闭包会导致性能问题。2、闭包会在父函数外部,改变父函数内部变量的值。
- 之前一直把匿名函数的概念和闭包弄混。
- 闭包本质上还是一个可以在外部访问到的函数。匿名函数只在解释到的时候立即执行,外部访问不到。
- 匿名函数一般用作块级作用域。
- 可以减少闭包占用内存的问题。
(function(){
// 块级作用域
})();
暂时没有完全弄明白,待日后有空再回头看,大概就是私有变量和方法外部不能直接访问。但是可以通过定义公有方法来访问。