[TOC]
题外话:其实我觉得能后通过自己感兴趣的事情给自己获取资源是一件很幸福的事情,这也是自己未来努力的方向吧也有人反对浏览器使用javascript,认为其不安全, 所以就禁用了javascript.
但其实我倒是觉得很历史倒退, 没有必要完全, 因为你可以看到其实浏览器大前端这一模块也是一个趋势, 而javascript就是一个最好的武器, 包括谷歌最近在开发的Fuchsia也打算在底层嵌入javascript, 所以我认为javascript在短时间内有可能成为趋势, 大前端的趋势还会攀升
申明笔记来自《JavaScript高级程序设计》第三版, 内容扩展来自冰山工作室的录音进行参考学习
JavaScript复习笔记一
JavaScript其实开始的时候是为了处理表单字段而写的,为了和Java很像但是又不能一样就导致了有很多保留字和java很像的情况。
JavaScript是由三个部分组成的ECMAScript(欧洲计算机制造协会),DOM(文档对象),BOM(浏览器对象)组成的
ECMAScript由ECMA-262定义,提供核心语言功能
DOM提供访问和操作网页内容的方法和接口
BOM提供浏览器交互的方法和接口
用let替换var可以很好地解决闭包问题
例如闭包:
// var
for(var i = 0; i < 10; i++){
setTimeout(function(){
// 每次都是10
console.log("var; "+i);
},10);
}
// let
for(let i = 0; i < 10; i++){
setTimeout(function(){
// 0,1,2...9
console.log("let; "+i);
},10);
}
很好理解, 就是var是全局作用域, let声明只在作用域中有效
白话:let我一定要等你 , 全局我先溜了
script元素
共有六个:
async异步下载, 主要用于控制下载的,支队外部脚本文件有效, 但是一般不用
charset主要用于解决乱码, 但是一般没有用到
defer控制外部脚本延迟加载, 也一般不用, 因为其实有时候也是会失效的在老版本浏览器,虽然现在版本兼容已经Ok了
src这个是最常用的, 不一定指向js类型, 其他外域文件也是ok的, 例如用到cdn加速的js也是可以的,但是我们跨域使用文件的时候要小心,一般不建议直接跨域吗,要不就找靠谱的大型CDN加速地址才可以,要不就下载到本地检查下安全性
type定义类型, 一般不写没问题, 常见是text/javascript也是默认值, 但是text/javascript和text/ecmascript也都不推荐使用了。需要了解的是服务器传送javascript文件的时候使用MIME类型是application/x-javascript。mime类型修改通常只在动态js中才用到, 但是我们在对于服务器返回类型的时候还是要注意下MIME返回的类型设定,因为有时候设定成XHTML之类的就会对值产生影响。
计算机的同步异步和我们日常的不一样理解,计算机同步是指一个人顺序做一件事情,异步代表多个人同时做事情。
javascript的异步操作其实本质上也是属于同步,因为它异步的是浏览器上开启多线程异步,javascript本身是没有这样的功能的。
javascript是解释型语言,解释型是指由预处理+执行两部分组成的,同时也是阻断式语言自己在做事情的时候不允许别人打扰。
对于<script>标签来说还是不要随便写内涵闭合标签的语句的好,要注意下,非要写动态js的时候也要注意有的地方要用到转移符之类的。
标签位置
我们一般把标签位置放在末尾, 但是也不是绝对的, 例如有关于页面渲染的脚本文件就要放在head处.
关于defer还是不用就好了, 因为我们有其他代替的方法例如文件存放顺序等来解决,并且一个html中最好只放一个延迟脚本文件, 因为外部的延迟脚本文件不是按照顺序的,因此有可能就会乱掉, 并且还要很重要的一点是, IE7并不支持defer
关于异步脚本async能不用也还是不用好了, 可以有,但是没有必要, 我们有其他代替的方法, 属性设置是async="async"
XHTML
可扩展超文本标记语言
比较严格, 例如不能用<要用<来代替, 需要注意的是我们在服务器返回文件类型定义 MIME的时候就要注意返回的类型, 要是返回XHTML就有可能导致我们和想要得到的值不一样,有可能被触发,例如: application/xhtml+xml
可以了解的应对方法, 虽然我们一般不会用到://<![CDATA[方式
<script type="text/javascript">
//<![CDATA[
...
//]]>
</script>
一般这个在外国的模板网站会比较经常看到, 但我们国内一般基本上不会看到, 包括大厂也比较少见
内嵌脚本和外部脚本优缺点
内嵌脚本优点就是速度快
在手机端编写的时候可以多用到内嵌脚本来减少与服务器之间的连接访问, 虽然连接访问不会慢,但是多了的情况下还是多少会影响速度的
外部脚本优点也有很多:
维护性会强一点
可以缓存, 如果不想要缓存就可以改个后缀名之类的,
名字相同的外部脚本文件只会下载一次-
适应未来,
通过外部脚本的javascript无需考虑到XHTML和hack问题hack就是兼容性问题
文档模式
分为混杂模式和标准模式
混杂模式会让IE行为包含非标准特性
这两种模式主要影响的是css内容的呈现, 如果浏览器开头没有发现文档声明就默认开启混杂模式
html:4s + tab键
html:5 + tab键会自动在文档开头写上文档模式例如:
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> </body> </html>
<noscript>元素
比较少用到, 因为现代浏览器很少不用到javascript
用于浏览器不支持<script>脚本的时候显示的内容
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<noscript>
<p>本页需要浏览器支持javascript才可显示</p>
</noscript>
</body>
</html>
对象
Object()可以把任何类型转化为对象,转化出来的叫原始值, 对于字符串的转化成对象会有多一个属性叫字符串长度:
Object('abc');
// 出来是 {0:'a',1:'b',2:'c',3:长度,4:'原始值abc'}
对象是以一种键值对的方式存在的, 对象里面也可以是属性名: 对象或function
对象是可以改变的, 原始数据类型不可以改变
对象内部的方法有constructor, hasOwnProperty(propertyName)检查是否纯在指定属性, isPropertyOf(Object), 还有一个用于检查对象是都可以使用for-in语句的propertyIsEnumerable(propertyName), toLocaleString(), toString(), valueOf()
对象类型有三种:
-
内部对象17种:
错误的对象(例如抛出异常之类的)
-
常用对象8中:
数字, 字符串, 布尔类型
数组, 时间, 函数, 对象, 正则表达式
-
内置对象:
marth, globle, json
数组对象,
经常用到的有windows, document两种自定义对象: 不属于前两种的对象都叫自定义对象
比较对象都会是不同结果, 对象之间不可以直接比较, 但是
可以比较对象的引用
把对象转化为原始数据类型的时候:
Boolean(Object)出来结果都会是true
Boolean(new Boolean(false)); ==>
true
Object.toString(): 转化为字符串:
数组返回'...', '...', '...'function就返回源代码,时间返回日期形式的字符串,正则表达式返回表达式本身包括转义符
Object.valueOf(): 转化为数字, 会直接查看原始值来返回结果, 没有原始值就返回对象本身, 对于时间对象就会返回一串数字, 其中包含1971年至今的毫秒数
对象的创建
有三种方式:
-
对象直接量var person = { name: 'sansen', job: function { alert('eating'); } }需要注意的是,
最后一个属性结尾不要加逗号不然在IE老版本里面会报错 new Object()如果没有传入值的话, 括号是可以省略的, 但是最好不要Object.defineProperty(对象, 属性名字, 对象描述)
一般我们不怎么用到这样的方法对象描述是指包含一些描述性信息, 例如该对象是否可以被继承之类的.
对象的检查值
有两种方式:
Object.nameObject['name']
这两种方式都会先判断Object是否为null或undefined,如果是的话就直接报错, 不是的话就看它是不是对象, 不是的话就先转化为对象
JS的6种表达式
-
原始表达式
常量不变的量, 其实非要变化也不是不可以, 如果一定不要改变可以设定成ES5中的define之类的,一般常量采用大写的形式来表达, 例如PI变量直接量包括: 数字, 字符串, 正则表达式关键字 -
初始化表达式
例如
var person={},var arr = [] -
函数定义表达式
function name(property){} -
函数调用表达式
fu(); -
属性访问表达式
Object.name之类的 -
对象创建表达式
new String('string')之类的
一元操作符
只能操作一个表达式的符号
对数字本身做一元加+numer还是会等于数字本身
一元加的作用
可以把类型转化为数字, 例如:var boolTest = true; +boolTest; // boolTest转化为了1, 其实和Number()有点像一元减其实和一元加有点像, 就是会先转化为数字类型, 然后再对数字类型进行负值操作
+0 === -0正零和负零是完全相等的
运算优先级
a = 1;
b = a +++ a; // 结果是3, 其实等于 b = a++ +a;把a++看成c=a++;所以为1 + a;现在a为2,因为为1+2;==>b=3
a = 3;
+++a == 3; // 结果是4!=3 ==> false
var a = 3;
!a++; // 结果是布尔类型, 因为一元运算是从右向左的
var a = 1;
b=a++ + ++a; // 1 + 3 ==> 4
运算顺序:
一元运算,三目运算,赋值运 算这三种是从右向左的, 其他运算顺序都是从左向右
运算优先级15种
属性访问的优先级最高
其次是一元运算符
乘除 --> 加减 --> 比较 --> 相等 --> 与或非 --> 三目运算 --> 赋值运算
赋值运算的优先级可以当做最低, 虽然逗号比他还低, 但一般比较少用到逗号运算
注意自减问题
自减的话很容易出问题, 例如:
1.1自减的话不是等于0.1
对此解决方案是: 先把他转化成整数再进行操作再转化回去
位运算
其实在我们平时开发当中我们几乎是用不到位运算, 比较有可能用到的
是按位非: ~num代表取反减一先转换为数字再取反再减一
逻辑非: 先转化为布尔类型再非
可以通过逻辑非来转化为布尔类型, 用
!!来进行转换
在布尔类型中只有六种可以转化为false其他都为true
六种分别是:undefined null 0 -0 NaN 空字符串
空对象转化为布尔类型也是true
按位非
按位与全1才是1
按位或有1就可以为1
按位异或不同就为1
字符形式的操作符
typeof这三个都是一元操作符, typeof后面的括号可以省略, 但是最好不要省略typeof不是函数是一元操作符
void返回的是一个undefined, 通常用于禁止跳转
delete用于删除属性
二元操作符
instanceof
auto instanceof Object // 判断类型属于
in
逻辑运算
&&逻辑与
var a = 1;
var b = 2;
// === 用于判断
if (a === 1 && b === 3) {
alert('well done');
}
那么与运算还可以做什么?
其实也可以做执行前的判断, 例如短路运算: 左边是一个判断, 右边是一个执行, 也就是说判断过了第一个正确后就会执行第二个
要注意的是与运算返回值
不一定是布尔类型, 是后面执行的返回值, 因为判断本身就是返回一个布尔类型所以很多时候别人会误以为是因为逻辑与返回的是布尔类型, 其实可以返回后面执行函数的结果的
遇到未定义的值直接报错
优先级逻辑与, 逻辑非, 逻辑或, 三目运算逻辑非是一元操作符拥有最高优先级
逻辑非 > 逻辑与 > 逻辑或 > 三目运算
左右移
<<左移乘2, <<<无符号左移
>>右移除2,>>>无符号右移
被0除结果返回的是Infinity/-Infinity
对两个对象进行相加是没有意义的
==和!=是先转化再比较, 但是===不会转化, 类型不相等就直接报错
null == undefined 返回true
null === undefined 返回false
字符比较的时候比较的是第一字符的ASCII码, 区分大小写
逗号操作符
逗号操作符可以在一条语句中执行多个操作
var num = (5, 1, 4, 8); // num == 8
tips显示类型转化就是类似
Number()这样的转换隐式类型转换, 就是例如字符串中的的:
string = "..." + number;
比较
如果两个对象进行比较, 会把对象返回原始值进行比较
比较的本质是对值的比较
对象比较
对象就像一座楼房一样, 同一个引用就是说, 在同一个地方的意思所以就有任何连个独立的对象都是不相等的说法因为在内存中是不一样的
先调用
valueOf()再调用toString()
对于我们无法确定的比较的时候, 应该注意先转换为显示类型比较稳妥
如果判断对象的时候, 其实判断的是对象的引用只有当这两个对象是同一个的引用的时候对象才相等,不然都是不等的
对于赋值运算不建议用连续赋值
对象转化成字符串的时候, 比如要把对象转化成数字类型,就会先调用valueOf方法, 如果返回的是一个原始值就把值返回回来, 如果返回的不是就去调用toString方法, 如果toSting出来的还是一个对象那么就会报错
空的数组转化为数字的时候得到的结果是0, 空的数组也是一个对象, 数组调用valueOf返回的还是数组对象本身, 那么是对象就继续调用toSting方法, 得到一个空的字符串, 空字符串转化为数字就是0
空的对象转化为数字为NaN调用valueOf()转为对象, 对象再进行toString方法出来是['Object']字符串, 字符串转为数字就是NaN
toString方法
toString是可以接受参数的,2-36进制,
Math.random().toString(16)会随机生成一串字符, Math是大写的
写不写var问题
不写var就会变成全局的属性, windows的全局属性
不用var的变量是可以被删除的用delete可以删除windows的属性
但是使用了var的就不可以被删除
在date对象如果要转化成Number不是调用的valueOf而是直接调用toString
**两个空的数组相加会生成空的字符串: **空数组会先调用valueOf方法,调用完后生成的是空的对象, 空对象调用toString后会转化成空的字符串
语句
表达式 | 语句 | 作用 | 副作用
表达式就是短语, 可以计算出计算结果
语句是一种命令, 会出事某个事件发生
作用就是, 例如二元加这种有我们直接想要结果的作用
副作用就是例如递增和递减, 是会转化对象的, 这就是副作用
表达式不一定会有副作用, 但是语句一定是有副作用的
隐式转换
在JavaScript中,加法的规则其实很简单,只有两种情况:你只能把数字和数字相加,或者字符串和字符串相加
[] + []; // 空数组相加为空
[] + {}; // 空数组加空对象为 [object Object]
{} + []; // 结果为0
{} + {}; // 空对象相加为NaN
空数组相加结果是0 ==> valueOf转为
数组对象本身, 然后再调用toString转为空字符串相加等于
空字符串加上[object Object], 因为空对象转换成字符串是这样的.
> String({}) // 空对象先调用valueOf出来空对象自己本身, 然后再转为字符串是 '[object Object]'对象加上[]是这样的
{} 写在前面会被当成是一个代码块, 那么就是 + []一元操作将[]转化为Number, 结果就是0
{} + {} 结果是 NaN是因为, 第一个被当成是代码块, 第二个是一元操作 + {} 就是把[object Object]转化为Number类型就是, NaN
标签语句
标签语句其实配合起来break还是很好用的, 在开发中我们尽量不适用continue, 标签写法:
strat: for () {
// start就是标签
break start;
}
with语句
定义with语句是为了简化多次编写工作位于同一个对象, 写法:
var qs = location.search,substring(1);
var hostName = location.hostname;
// 写成with就可以写成一个代码块封装起来
with(location) {
var qs = search.substring(1);
var hostName = hostname;
}
但是我们在开发中尽量避免使用with语句, 因为他会对性能造成影响
函数
首先所有函数都有返回值, 其次js中没有对参数个数进行限制(虽然实际上也是存在限制的, 但是数组太大了基本可以认为是无限制)
如果不写参数, 但是传入了参数, 可以使用argument[i]来获取参数参数长度对应可以使用argument.length来获取
ps
变量保存的知识一个地址, 具体的类型直到运用才会进行判断类型
js本质上还是顺序执行的, 因此下面的代码会覆盖上面的代码, 其实大部分语言都是顺序执行的没有什么差别
复制对象的时候其实是复制对象的引用, 因此修改对象属性也会影响到原始对象
JavaScript不可以直接访问内存中的位置, 操作对象的时候操作的是对象的引用而不是实际对象
关于复制变量值
和c++不同的是, JavaScript的值是完全独立的, 但是c++如果他们的值相同就代表使用的是同一个地址, 例如:
var num1 = 5;
var num2 = num1; // 其实这里num2叫做副本, 与num1不是同一个内存地址.因此互不影响
基础类型是通过值传递的, 并且基础类型没有方法, 只有对象才有方法
JavaScript中没有块级作用域的说法在之前, 但是ES6以后就引用进来了, JavaScript之前只有
函数作用域,也就是说局部变量的作用域是在函数体中的
类型检查
typeof是检查基础类型, 它无法检查对象类型, typeof其实也是一元操作
instanceof检查对象类型但是无法检查基础类型, a instanceof b
声明变量
var声明的变量会自动加载到最近的环境中, 并且放在顶部, function声明也是放在顶部
不加var就代表是局部变量
垃圾回收
JavaScript中的垃圾回收是自动进行的, 隔一段时间处理一次
手动垃圾回收把不用的变量设置为null过一会就会被垃圾回收了
基本数据类型因为是在内存中占用固定大小因此会被保存在栈内存中
引用类型的值是对象, 保存在堆内存中
对象类型传入属性
传入属性
- 可以使用对象字面量来加入属性, 然后用
object.name来引用属性 - 可以通过
object[name]来引入属性, 这样有一个好处是,可以传入变量参数
Array类型
ems的Array类型很强大, 他是动态的, 用[]传入数组, 数组最多包含42亿接近43亿的项
数组的length不仅仅只有只读的作用, 也可以修改长度
数组的参数最后一个位置不要加
,因为在ie7中会报错
- length设为过长, 那么后面的数组就会自动创建值undefined
- length长度设为过短, 就会切除掉后面的数组保留前面对应长度的数组
slice()切分数组
- slice(开始位置, 结束位置)
- slice(开始位置)
如果传入的是负值代表的就是倒数位数, 类似数组长度加上负值
isArray
可以用Array.isArray(arr)来判断是不是数组
对于空的数组和空对象,
valueOf返回的是对象本身
join()
var color = ["red", "blue", "yellow"];
color.join('-'); // "red-blue-yellow"
splice()
splice非常强大, 可以有很多的参数, splice(起始位置, 要删除的项目数, 要插入的值)要插入的值这一步的可以有很多值,类似"xxx", "xxx", ...这样所以说有很多参数, 但是其实可以看成三个参数
栈和队列
对于栈: pop()表示尾部输出, push表示尾部加入
对于队列: shift()表示头部输出, unshift()表示头部加入
判断是不是数组
用instanceof
用constructor,
Object.constructor === ArrayArray. isArray(arr), 不过只支持es5以上
Object.prototype.toString.call(arr) === '[object Array]'这个是非常重要的!!!,ES5内部其实也是这么判断的
数组重新排序
sort()
默认是从小到大, sort()可以传入函数, sort其实是冒泡排序, 自己写的function目的是告诉排序位置, 是一个个位置进行比较的, 就是类似第一个和第二个先比较, 然后看下有没有换位置, 如果有换位置就换下位置然后从头再开始, 如果没有交换位置就继续第二个和第三位置进行对比, 这样一直下去直到排序完成, 类似;
function compare(value1, value2) {
if (value1 < value2) {
return -1; // -1代表第一个参数在第二个参数前面
} else if () {
return 1; // 1代表第一个参数在第二个参数后面
} else {
return 0; // 代表位置不变
}
}
var value = [0, 1, 5, 10];
value.sort(compare); // 传入自定义函数
冒泡排序的时间复杂度是n~n^2, 对于默认的sort函数, 从小到大排序好的时间复杂度就是n, 但是从大到小逆排序的时间复杂度就变成了n^2
因此对于逆排序的进行从排序的数组要采用reverse()反转数组的快很多
对于排序要视情况来选择算法
例如: 对于很大的数组进行排序, 则可以考虑先把他们进行快排, 选定一个数字, 大于其的排在右边, 小于的排在左边, 然后对小部分在进行sort排序会快很多
数组迭代方法
有五个迭代方法, 并且其中函数的参数都三个分别是 item index array , 我们可以这么看:
只返回一个值的, true或者false
- arr.every(function...) 如果每一个值都符合函数的return判断那就返回true
- arr.some(function...) 数组有符合函数的return判断就返回true
返回筛选结果的
-
arr.filter(function...) 通过filter返回符合筛选结果的数组
例如: function(item, index, array){
return (item > 2);
}
arr.map(function...) map也是类似返回, 不过
map的return是返回运算, 例如item*2来对数组的所有值进行一遍映射
没有返回结果的
- arr.forEach(function...) 对数组的每一项进行运算处理, 不一定有映射处理, 没有返回值
如果要兼容到低版本的ie话, 数组就只能运行十个方法:
push pop shift unshift reverse sort join concat slice splice
数组归并方法
也就是有点像把操作好的和下一项移动进行的意思, 理解为贪吃蛇
- reduce() 从左到有
- reduceRight() 从右到左
function参数有四个, 至少保留前两个, 分别是: prev cur index array
