##表示每一章,####表示每一个小节
书 P35的递归方法、书55页部件
注:字面量表示如何表达这个值,一般除去表达式,给变量赋值时,等号右边的就可以认为是字面量
jq的核心思想是单例、工厂模式;vue,react的核心思想是观察者、MVVM模式。
var test = "hello world!"
这里 “hello world!”就是字面量,test是变量名
在js中,字面量分为字符串字面量,对象字面量,数组字面量和函数字面量
##补充
- js中的apply和call的区别,两个方法的第一个参数都是当前调用的函数的调用环境(this),而apply是将所有参数放在一个数组中,但是会自动拆分(只会拆分一次)。而call是有多少个参数就放多少参数,不会自动拆分
##第二章 语法
####语句
- 如果函数没有写reture,那么会默认返回undefined
- typeof 会产生的类型 number,string,boolean,object,function,undefined,但是当时一个数组或者使null时,返回的是object,这其实是不对的
- 对象字面量就是指包围在一个花括号中的零或多个键值对,对象字面量可以出现在任何允许表达式出现的地方
- 检索对象里面包含的值bookinfo[“item”]
##第三章 对象
####检索
- javascript的标识符中不能包含连接符(-),但是可以包含下划线(_)
- 一个机智的写法 bookinfo.item && bookinfo.item.child,不会因为item属性不存在而抛出异常
####引用
- 对象通过引用传递,对象永远不会被复制
####原型
- 原型连接在更新时是不起作用的,所以对某个对象作出改变时,不会触及该对象的原型
- 委托可以理解为,在一个对象中找某个属性时,如果这个对象没有,那么在它的原型中继续找,如果也没有,那么继续往上找,直到object.prototype
- 当我们添加一个新的属性到原型中,那么所有基于这个原型创建的对象对该属性都会可见
- hasOwnProperty方法不会检查原型链,只会检查该对象是否独有这个属性
####减少全局变量的污染
全局变量削弱了程序的灵活性,应该避免使用,最小化全局变量的方法就是只创建一个唯一的全局变量,这样与其他应用程序或者页面冲突的可能就会明显降低,还有一种有效减少全局变量的方式方式就是闭包。
var mappy = {};
##第四章 函数
####函数对象和字面量
- 函数对象连接到Function.property(改原型对象本身也连接到object.property)
- js的函数被创建时,会有一个调用属性,当你调用函数时,其实是调用了这个‘调用’属性
- 通过函数字面量创建的函数对象包含一个连到外部上下文的连接,这被称为闭包
####函数的调用方式
- 函数的四种调用模式,这四种模式在如何初始化关键参数this上存在差异
- 方法调用模式,可以理解为在对象中的属性,这个时候就不是函数,而是方法了,此时this指向当前的对象
- 函数调用模式,此时this会绑定到全局(这是语言设计上的一个错误),需要定义一个that变量来存储之前的this
- 构造器调用模式,使用new来调用,那么就会创建一个连接到该函数property成员的新对象,同时,this也会绑定到这个新对象上
- apply调用模式,需要两个参数,一个表示要绑定的this值,一个表示传递的参数数组,this传null默认绑定到window变量
####返回
- 函数调用时若用了new,则返回当前的this(该新对象),若不用new 调用,且函数没有return的话,则返回undefined
####异常
- 在类库混用时,可以用异常进行确保没有该方法时才进行添加
####作用域
- js不支持块级作用域(任何一对花括号中的代码{}都是一个块,在js中,在块内用var声明的变量可以在块外访问到,但是在es6中,let和const则是支持块级作用域的,let其实也是支持变量提升的,只是提升到函数的顶部,但是没有初始化为undefined,所以在提升的顶部到变量初始化之前的区域都不能使用,这就是暂时性死区),所以使用var的话,最好就是在函数顶部吧变量都声明
- 避免在循环中创建函数,这样只会带来无意义的计算并且引起混淆
####模块
- 模块是一个提供接口却隐藏状态与实现的函数或对象(有点类似java类的私有变量)
- 可以使用函数和闭包来构建模块
- 模块模式的一般形式:定义了私有变量和函数的函数,利用闭包创建可以访问变量和函数的特权函数,最后返回这个特权函数即可。
- 通过模块,几乎可以完全摒弃全局变量的使用。(javascript最糟糕的特性之一 ———— 全局变量)
####级联
- element.width(‘100px’).height(‘100px’)就是一个级联,每次调用的结果都会被下一次所调用,所以没有必要写太过全能的接口,一个接口没必要一次做太多的事情。
####记忆
- 将先前操作的结果记在某个对象里,从而避免重复的计算
##第五章 继承
- proto: 这个属性是实例对象的属性,每个实例对象都有一个proto属性,这个属性指向实例化该实例的构造函数的原型对象(隐示原型,通过这个属性增加方法的话,需要添加到下面的constructor属性里添加)。
proterty:这个方法是对象的属性。(据说和一个对象的attr类似,比如dom对象中)
prototype:每个构造函数都有一个prototype对象,这个对象指向该构造函数的原型。(显示原型)
- 常用的几种继承方式
- 伪类,基于类继承,通过修改子类的prototype去继承父类的属性和方法(在子类的prototype上增加方法和属性不会修改父类的)
- 基于原型的继承(不会对父原型改变)
- 使用函数化实现集成,运用块模式,实现变量的私有化
第六章 数组
- js中没有数组,但是js有一些类数组也行的对象
长度
- js中的数组的length是没有上届的,若用大于当前数组长度的下标去存储,那么当前数组会自动拓展到那个长度
删除
- 使用delete删除之后会在原来的位置保留一个空洞 [1, empty, 1, 1, 1]
- 使用splice对数组进行删除,但是这对于大型数组的效率不高
容易混淆的地方
- js中对于数组和对象的区分并不严格,当属性名是小而连续的时候,应该使用数组,否则,使用对象。
- typeof 数组,结果是‘object’,所以需要自己实现对于当前对象是否是数组的判断
- 使用arr.totle=function(){}是可以的而且不会改变arr的length
- 使用Object.create()方法对数组是没用的,因为复制出来的对象而不是数组
第七章 正则表达式(本章基本跳过,没有深入学习)
- 可处理正则的几个表达式regexp.exec,regexp.test,string.atch,string.replace,string.search和string.split
- 正则表达式的标识
- g 全局匹配,不通的方法对g标识的处理不太一样,regExp的exec方法和test方法,不建议使用test方法,再比如string的search和match方法,search方法会忽略g
- i 大小写不敏感
- m 多行(^和$匹配行结束符)
- 分支,使用|表示分支,会按顺序进行匹配,只要匹配一个就表示匹配成功
第八章 方法
Array
- array.concat(item…),产生一个新数组,包含一份array的浅复制,后面的item会被依次添加
- join
- array.pop()使得数组像堆栈一样工作,移除数组的最后一个元素并且返回,如果没有最后一个,那么返回undefined
- push 该方法会返回数组的length
- array.reverse()反转数组并且返回
- array.shift()删除数组的第一个元素,并且返回该元素,如果为空,那么返回undefined,shift通常比pop慢得多(因为删除第一个并且要讲剩下的元素顺序前移)
- array.slice(start,end)字符串的截取,如果其中任何一个是负数的话,那么array.length会和他们相加,如果还是负数,那么返回一个新的空数组,注意个splice方法的区分,直接写一个负数表示取到最后几位
- array.sort()数组的排序,但是因为排序之前会先转换成字符串,所以不能正确的对数字数组进行排序,当然,你可以自己重写排序方法内的逻辑
- 对象的比较见书P80
- splice(start,count,item….)删除之后,item会替换原来删除位置的元素
- array.unshift(item…)插入元素到数组的开头,并且返回length
String
- chartAt(pos)返回pos处的字符,如果没有,返回空
- charCodeAT
- concat
- indexOf
- lastindexOf
- localeCompare(that)类似array.sort,比较两个字符串,小于为-1,等于为0,大于为1
- match(regexp)根据正则表达式进行匹配,根据g表示决定如何匹配
- replace
- replaceAll
- search(regexp)类似indexof,只是接受的是一个正则表达式
- slice(start,end)复制字符串从start到end-1位置的字符,和array.slice类似,没有则返回一个新的空字符串
- split(separator,limit)根据separator对string进行分组,limit是可选参数,表示分组的限制
- substring(start,end)和slice一样,但是不能支持负数,所以建议使用slice,不建议使用substring
- toLocalLowerCase()用本地规则吧string中的字母转换成小写,主要是用于土耳其语‘I’转换为‘1’
- toLocalUpperCase()用本地规则吧string中的字母转换成大写,主要是用于土耳其语‘i’转换为向量I
- toLowerCase()转为小写
- toUpperCase()转为大写
- formChartCode(item….)将一串数字编码(Ascii)返回为一个字符串
##代码风格(代码不光是给计算机运行的,也是给人看的)
- if while 这样的结构化语句中,要始终使用代码块,很有可能if(a)b;变成了if(a)b;c;一对花括号可以用低廉的成本去防止可能出现的BUG
- 使用K&R风格,吧{放在一行的结尾而不是下一行的开头,这样可以避免js return语句的一个可怕的设计错误
- K&R风格
- 在合适的位置换行(比如在一个超长的表达式中,在两个表达式之间断开)
- 在二元运算符的位置换行也是可行的
- 当参数较多或者参数较长时,多行往往比一行更加直观醒目
- {}写紧凑,{在一行的末尾,}也是
- 多个不同意义但是相同类型的变量最好分开定义和注释
- 代码不要过于拥挤,注意添加空行
- 弹幕运算符一般不需要空格
- 双目,三目运算符的操作数之间要加上运算符
- 代码要保持好缩进,保证可读性
- 避免那些看起来可能有错误的语法(变量提升,if(a=b)等等)
- 减少对于全局变量的使用,随着程序的日益复杂,全局变量的问题会逐渐变得问题重重
##js中的毒瘤
全局变量
- 全局变量
- js中的全局变量是所有程序都可以随时访问和修改的,这是一个很大的问题,也很容易产生BUG
- 当两个程序中的全局变量产生了冲突时,会产生BUG,而且这样的问题很难被发现
- js中所有的全局变量在编译后都将载入一个公共的全局变量中
- 定义全局变量的三种方法 var foo = value; window.foo = value; foo = value
####作用域
- 虽然js是类C语言,但是没有块级作用域,也就是说,在{}外可以访问到{}内定义的变量,但是在es6中let,const可以有效避免这样的问题
####(使用模块设计可以有效避免上面的两个问题,减少全局变量的污染和作用域的问题,同时还能提供私有变量)
####自动插入分号
js中存在自动补全分号的机制,但是别指望他能干什么好事,很多情况下都是产生难以检查的BUG
####保留字
当用保留字进行变量的定义时,需要使用引号
####typeof
typeof null //object
typeof value //value为数组,结果为object
####parseInt
parseint将字符串转为int,但是遇到非数字时就会停止,且当为0开头时自动转换为8进制所以当使用这个函数时,最好手动指定转换的进制parseInt(value,10)
+
+号会自动对两个操作数进行类型转换,所以当进行+操作时,请确保两个操作数都是数字
####NaN
坑
NaN === NaN //false
NaN !== NaN //true
- 使用isNaN函数对数字与NaN进行辨别
- 使用isFinite函数对是否是数字进行判定,但是它会自动将运算数转为数字
自定义isNumer函数
var isNumber = function(value){ return typeof value === 'number' && isFinite(value) }
伪数组
要检测一个对象是否是数组,使用typeof是不够的还需要检查他的constructor属性
//最保险的做法 if(Object.prototype.toString.apply(my_value) === '[object Array]'){ //my_value确实是一个数组 }
- 函数自带的arguments参数不是一个数组对象,只是一个有着length成员属性的对象
####假值
- js中的假值
- 0
- NaN
- ‘’
- false
- null
- undefined
- undefined 和 null并不是常亮,但他们是全局变量(在es5中明确规定为全局变量),在主流的浏览器中都无法改变他们的值,但是ie8及以下可以
####hasOwnProperty
不要替换hasOwnProperty的值
####对象
js中因为原型的存在,所有永远不会有空对象,所以在某些情况下判断属性的时候需要注意
##附录B 糟粕
####==
避免使用==,他会自动对比较的两遍进行转换,比对出很对莫名其妙的结果,这会让你的程序变的有些不可控
####with
和==一样,他会产生一些不可控的结果,所以避免使用
####eval
传递一个字符串给JS编辑器,并执行其结果,使用它会让代码变的难以阅读,因为它需要运行编译器,而且会让jsLint失效,并且会减弱程序的安全性,因为它给了被求值的文本太多的权利,而且还降低了语言的性能,在setTimmeout和setInterval函数中,若传递的是字符串是,会像eval一样去处理,同样也要避免这样的参数传入
####continue
经过测试,代码重构之后,将continue去除,都会使代码的性能得到改善
####缺少块语句
在if while for语句中,尽管只有一行逻辑,也要把{}加上,防止逻辑出错
####++ –
这两种运算符鼓励了一种不够严谨的代码风格,大多数缓冲区溢出错误所造成的安全漏洞都是这样导致的
在实践中,使用++和–时,往往会导致代码过于拥挤,复杂和隐晦,这样会让代码变的难以阅读和理解,所以,可以尽量避免使用他们
####位运算符
大多数语言中,位运算符接近于硬件处理,所以非常快,但是js中很少接触到硬件,由于js中没有整数类型,所以位运算符会先转换成整数进行运算再转换回去,而且由于js接触不到硬件,所以位运算符非常慢
####function语句和function表达式
语句 function test(){} 表达式 var foo = function foo(){}
function语句在解析时会发生被提升的情况,会被移动到定义时所在的作用域的顶层。这样可能会导致混乱
一个语句不能以一个函数开头,包裹在()之中就可以了(function())
####类型包装对象
new Boolean(false)会返回一个对象,该对象的valueOf方法会返回包装对象的值,这完全没有必要,包括其他基本类型的new也应该避免
new
如果应该是用new初始化而没有的话,那么就是声明一个普通的对象,而且对象中的this就会绑定到全局变量,这样会污染全局变量
一般使用大写开头来区分构造器函数,但是最好避免使用new来引发混乱
####void
void在js中是一个运算符,接受一个运算数并且返回undefined,这并没有什么用,但会令人困惑