JavaScript学习笔记

js是一门基于对象的弱类型语言,至于用处可谓是无处不在,GitHub语言排行榜稳稳的第一,JS终究一统世界…不来学习下?起码前半部分还是java呢!虽然和java并无啥关系

还有一个原因是,这几天微信小程序弄的沸沸扬扬,我也漫无目的的去学习了下,也涉及到了js,微信小程序已经内测,你的js知识储备准备好了么??o( ̄▽ ̄)ゞ)) ̄▽ ̄)o
写的比较混乱 = =!因为感冒嘛~

开始

js一般要和html、css配合起来用,所以还是要有一些html之类的知识的,嘛~不就是一堆标签吗?学习下也用不了多长时间,js的代码我们一般可以写在两个地方:

  • html文件中
    <script type="text/javascript">这里写js代码</script>
    可以放在页面的任何位置,但是我们一般放在网页的head或者body部分。
    如果在head部分:浏览器解析head部分就会执行这个代码,然后才解析页面的其余部分。
    如果在body部分:网页读取到该语句的时候就会执行,执行完后再读取下面的
  • 外部js文件
    写在单独的扩展名为.js的文件中然后在html中引用,js文件中不需要再加标签,引用方式:<script type="text/javascript" src="script.js"></script>,注意的是标签中间不能再写js代码,如果需要补充代码的话可以再写一个标签。

一般来说,script 要放到 body 的最下面,提高加载速度,如果放到 head 里那就会影响 HTML 结构的渲染,理论上说放那都行,但是规范上 js 属于正文部分,所以最好是放在 body 里面。
但是呢,如果页面引用了多个文件,不能保证在用时已经加载完毕,所以很多选择性放在头部还是尾部。
http://sfau.lt/b5saPH

基础语法

很多地方其实还是和其他语言有很大相同点的,这里主要总结一些和其他不同的地方,因为也没学过什么解释型语言。
Js中有一种叫严格相等===也就是比较对象而不是数值

变量

js是弱类型语言,所以变量的类型不会像java那样细分,定义变量只有一个关键字 var使用就是:var 变量名
理论上名称是可以任意取名,同样最好遵守下命名规范:

1.变量必须使用字母、下划线(_)或者美元符($)开始。

2.然后可以使用任意多个英文字母、数字、下划线(_)或者美元符($)组成。

3.不能使用JavaScript关键词与JavaScript保留字。

更详细的在这:https://github.com/fex-team/styleguide/blob/master/javascript.md

注意:
变量也可以不声明,直接使用,但为了规范,需要先声明,后使用,不声明的话就会是(隐式)全局变量[定义全局变量的正统方式]
当变量没有初始化就被使用 就会显示undefined
js 中 false、null 就是0,非 0 就是true 是可以运算的如: false + 1 = 1 ; true + 1 = 2
或者说,null、0、undefined、’’(空字符串) 这些都是 false,可以(可能是部分浏览器)通过 if 判断

函数和属性

因为js中没有class(类)的概念,所以函数很重要!基本定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//常规定义
function 函数名()
{
//函数代码;
}
//函数的参数不用特意声明
function add(x,y,z){
x = 4;
}
//动态函数,a、b是参数,后面是运算逻辑
var x = new Function("a","b","return a+b");

//与其java类似的一个需要注意的问题
var a = 3;
show(a);
function show(x){
x = 5;
}
//最后的结果a还是3,注意它们的范围

以及一个简单的调用:<input type="button" value="点击我" onclick="funName()" />
注意:
js中的函数没有重载(函数其实就是一个对象,对象只有覆盖),所有的参数被函数内的arguments所接收,即使是空参的,所以说空参的函数你也可以向里传参数,同时也可以传部分参数,你没传的参数默认就是undefined了
js中函数就是对象,var x = show 不加括号的话相当于直接指向了那个对象,加括号相当于进行了运算,然后把运算后的值给x(和最近学的Py好像)
可以被封装起来用
JS中所有.的调用都可以替换成[''],他们是等价的,在拼接的时候只能用第二种方式,下面两句是等价的:

1
2
obj.prop = "abc";
obj["prop"] = "abc";

函数还可以封装起来当作java中的class来使用,涉及到定义属性,JavaScript中有三种不同类型的属性:命名数据属性,命名访问器属性以及内部属性,关于属性其实上面多少已经使用了一些,几个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function Test(name){
this.name = name;
}
var a = new Test("loli");
//属性可以直接赋值不用定义
a.item = 1;
a.t = "aaa";
alert(a.t + a.name);

//函数和方法的区别
var arr = [1,2,3,4,5]
var a =12; // 变量:自由的
arr.a= 5; //属性:属于一个对象
function show() //函数:自由的
{
alert("a");
}
arr.fn = function() //方法:属于一个对象
{
alert("b");
}

其实还可以定义一个空函数用来做构造器,构造函数首字母一般大写,用来区分

1
2
3
4
function Class(){}
//设置原型
Class.prototype={};
var item=new Class();

这里首先声明下,js中的{}一般代表定义一个对象,大部分情况下是要有成对的属性或值,或函数

命名数据属性

这个是我们通常所用的“普通”属性,感觉挺像json的格式的

1
2
3
4
5
6
7
8
9
var obj = {
prop: 123
};
//读取(获取)值
console.log(obj.prop); // 123
console.log(obj["prop"]); // 123
//写入值
obj.prop = "abc";
obj["prop"] = "abc";

命名访问器属性

概括一下就是:借助函数来获取或设置一个属性的值

1
2
3
4
5
6
7
8
var obj = {
get prop() {
return "Getter";
},
set prop(value) {
console.log("Setter: "+value);
}
}

调用后的效果:

—>obj.prop
‘Getter’

—>obj.prop = 123;
Setter: 123

内部属性

有一些属性仅仅是为规范所用的,称之为内部属性,因为它们无法通过JavaScript直接访问到,但是它们的确存在,并且影响着程序的表现.内部属性的名称比较特殊,它们都被两个中括号包围着.下面有两个例子:

  • 内部属性[[Prototype]]指向了所属对象的原型.该属性的值可以通过Object.getPrototypeOf()函数读取到.该属性的值只能在创建一个新对象的时候通过Object.create()或者proto来设置 [1].
    例如:Object.getPrototypeOf(this).methodName1();
    可以看下面的对象原型标题
  • 内部属性[[Extensible]]决定了是否能给所属对象添加新的属性.该属性的值可以通过Object.isExtensible()读取到.还可以通过Object.preventExtensions()将该属性的值设置为false.一旦设置为false,就无法再设置回true了.

关于匿名函数

在javascript语言里任何匿名函数都是属于window对象。在定义匿名函数时候它会返回自己的内存地址,如果此时有个变量接收了这个内存地址,那么匿名函数就能在程序里被使用了,因为匿名函数也是在全局执行环境构造时候定义和赋值,所以匿名函数的this指向也是window对象。
这里为什么要单独说下匿名函数呢,因为它好玩啊!还可以这样玩:

1
2
3
4
5
6
7
8
//自执行函数
var result = function (){
alert(2);
}();
//或者这样写
var result = (function () {
console.log(2);
})();

with语句和for…in

  • with语句主要是为了调用对象的方法可以省略函数名

    1
    2
    3
    4
    5
    6
    function a(name){
    this.name = name;
    }
    with(a){
    name = "bbb"; //不用写a.name
    }
  • for…in 可以用来遍历对象。遍历出的是变量名(假设理解为key:values可以理解为key),如果取值直接用.获取值是不行的,没法进行字符串的连接,所以要用[]的方式来解决
    遍历数组得到的是角标,同样需要arr[x]取值

关于 for in 及循环来了解一下:

1
2
3
4
5
6
7
8
9
10
11
var array = [0,1,2,3,4,5,6,7,8,9];

// for-in 循环
for (var val in array) {
fn(val);
}

// for 循环
for (var i=0; i < array.length; i++) {
fn(array[i]);
}

在 for-in 需要分析出 array 的每个属性,这个操作的性能开销很大,用在 key 已知的数组上是非常不划算的。所以尽量不要用 for-in,除非你不清楚要处理哪些属性,例如 JSON 对象这样的情况。
在第二种循环中,效率也不高,循环每执行一次,都要检查一次 array.length 的值,读属性要比读局部变量慢,尤其是当 array 里存放的都是 DOM 元素(像 array = document.getElementByClassName("class");),因为每次读 array.length 都要扫描一遍页面上 class=”class” 的元素,速度更是慢得抓狂。
下面就介绍几种优化后的代码:

1
2
3
4
5
6
7
8
9
10
11
12
var array = [0,1,2,3,4,5,6,7,8,9];

// for 循环
for (var i = array.length; i--;) {
fn(array[i]);
}

// while 循环
var i = array.length;
while (i--) {
fn(array[i]);
}

这样就避免了每次判断 length 的尴尬情况。

其他补充

  • 关于回调
    js中的回调相比java就简单太多了,不需要接口啥的,因为变量可以指向函数嘛~所以到时候直接”执行”那个变量就好了嘛…
  • js中的全局函数
    escape( )、eval( )、isFinite( )、isNaN( )、parseFloat( )、parseInt( )、unescape( )等

更多关于函数的解释见:https://segmentfault.com/a/1190000000660786#articleHeader11

对象原型

js中很多内部对象是拥有prototype属性的,对于拥有prototype属性的对象可以在原型的基础上增加功能扩展。下面的例子可以很好的说明这个功能,我们在string对象的基础上增加去空格和转置的功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//去除前后空格
function trim() {
var start,end;
start = 0;
end = this.length-1;

while(start <= end && this.charAt(start) == " "){
start++;
}

while(start <= end && this.charAt(end) == " "){
end--;
}
return this.substring(start,end+1);
}

//转置函数
function reverse() {
var str = "";
for (var i = this.length - 1; i >= 0; i--) {
str += this.charAt(i);
}
return str;
}

//获取对象的原型,扩展功能,注意String的S的大写
String.prototype.trim = trim;
String.prototype.reverse = reverse;

DOM

DOM:document object model 文档对象模型,简单说:将文档和标签以及其他内容变成对象。
DOM分为三层模型
dom1: 将html文档封装为对象
dom2: 在leve 1基础上加入了新功能,比如解析名称空间。
dom3: 将XML文档封装成对象

对于dom1的封装,可以视为将html的标签的层级关系封装成了树形结构,称为DOM树,每一个节点(标签)都是一个对象,能更好的进行管理(比如创建、删除、修改),能够动态的改变html的结构。

MDN: 文档对象模型 (DOM) 是HTML和XML文档的编程接口。它提供了对文档的结构化的表述,并定义了一种方式可以使从程序中对该结构进行访问,从而改变文档的结构,样式和内容。DOM 将文档解析为一个由节点和对象(包含属性和方法的对象)组成的结构集合。简言之,它会将web页面和脚本或程序语言连接起来。

说白了DOM就是浏览器为JavaScript提供的一系列接口(通过window.documnet提供的),通过这些接口我们可以操作web页面(JS 无法直接修改页面结构)。 但DOM并不是编程语言,它是文档对象的模型,该模型是独立于编程语言的。

关于DHTML:
是多个技术的综合体,叫做动态的html
DHTML=HTML+CSS+JavaScript+DOM

补充:在DHTML基础上加了轻量级的与服务器交互的功能就成了AJAX,DHTML+XMLhttpRequest = AJAX,比如google的搜索词预测

关于解析

对于DOM解析方式
好处:可以对树中的节点进行任意操作,比如:增删改查。
弊端:这种解析需要将整个标记型文档加载进内存。意味着如果标记型文档的体积很大,较为浪费内存空间。

另一种解析方式:
SAX:是由一些组织定义的一种民间常用的解析方式,并不是w3c标准,而DOM是W3C的标准。
SAX解析的方式:基于事件驱动的解析。随加载,随解析(读取到一个标签的结束标记就进行解析)获取数据的速度很快,但是不能对标记进行增删改。

关于节点

比较常见的又三种DOM节点:

  1. 元素节点:上图中<html>、<body>、<p>等都是元素节点,即标签。

  2. 文本节点:向用户展示的内容,如<li>...</li>中的JavaScript、DOM、CSS等文本。

  3. 属性节点:元素属性,如<a>标签的链接属性href

节点名称(nodeName)
元素节点的 nodeName 与标签名相同
属性节点的 nodeName 是属性的名称
文本节点的 nodeName 永远是 #text
文档节点的 nodeName 永远是 #document
节点的类型(type):
标签型节点:1
属性节点:2
文本型节点:3
注释型节点:8
document:9 (它的范围就是浏览器中整个显示网页的区域)
获取节点的方式
获取的方法有多种,获取子节点、父节点、兄弟节点之类的….
注意:标签之间如果存在空行,有的浏览器会认为是一个空白的文本节点

获取元素

一般我们常用的就是三种:

1
2
3
4
5
6
//通过ID进行获取,window可省略
document.getElementById("id");
//通过标签名获取
getElementsByTagName("tagName");
//通过指定的名称
getElementsByName("name");

第一种返回的就是一个对象,后面的两个返回的是一个对象的集合,可以理解为数组,因为id只能有一个,其他的就不确定了。
获取到对象后可以根据API中的html属性所对应的DHTML样式属性(style)或者标签属性的名来修改内容。

其他

事件冒泡

微软提出了名为事件冒泡(event bubbling)的事件流。事件冒泡可以形象地比喻为把一颗石头投入水中,泡泡会一直从水底冒出水面。也就是说,事件会从最内层的元素开始发生,一直向上传播,直到document对象。
因此上面的例子在事件冒泡的概念下发生click事件的顺序应该是p -> div -> body -> html -> document

事件捕获

网景提出另一种事件流名为事件捕获(event capturing)。与事件冒泡相反,事件会从最外层开始发生,直到最具体的元素。
上面的例子在事件捕获的概念下发生click事件的顺序应该是document -> html -> body -> div -> p


后来 w3c 采用折中的方式,平息了战火,制定了统一的标准——先捕获再冒泡
以下属于DOM2级处理程序了,0级就是直接设置onclick属性,DOM0级事件只会在冒泡阶段加载!
addEventListener的第三个参数就是为冒泡和捕获准备的.
addEventListener有三个参数:

element.addEventListener(event, function, useCapture)

第一个参数是需要绑定的事件
第二个参数是触发事件后要执行的函数
第三个参数默认值是false,表示在事件冒泡阶段调用事件处理函数;如果参数为true,则表示在事件捕获阶段调用处理函数。
对于事件代理来说,在事件捕获或者事件冒泡阶段处理并没有明显的优劣之分,但是由于事件冒泡的事件流模型被所有主流的浏览器兼容,从兼容性角度来说还是建议大家使用事件冒泡模型。


下面的方法被对象所调用
type属性用于获取事件类型
target属性用于获取事件目标
stopPropagation()方法 用于阻止事件冒泡
preventDefault() 方法 阻止事件的默认行为
event.KeyCode 可用来屏蔽按键
event.returnValue=false 可以将事件取消掉(常用于表单)
event.srcElement 获取事件源对象(那一个对象触发的)

BOM

用于操作浏览器的对象,BOM的核心就是window对象,window对象指当前的浏览器窗口。
利用window对象可以操作浏览器的相关设置,比如地址栏、屏幕的大小、操作系统、窗口的位置等等,这个貌似不太经常用,这里不说了,用到了查API或者google之
不过一般会用到一些事件,如窗体装载、卸载啦

  • 加载事件(onload)
    事件会在页面加载完成后,立即发生,同时执行被调用的程序。

  • 卸载事件(onunload)
    当用户退出页面时(页面关闭、页面刷新等),触发onUnload事件,同时执行被调用的程序。

  • 关闭窗口
    window.close :关闭本窗口
    [窗口对象].close :关闭指定窗口

  • History 对象
    可以获取用户曾经浏览过那些网页,以实现后退、前进的功能

  • 其他常用对象
    location用于获取或设置窗体的URL,并且可以用于解析URL。
    Navigator 对象包含有关浏览器的信息,通常用于检测浏览器与操作系统的版本。
    screen对象用于获取用户的屏幕信息。

其他常用函数/事件/属性补充

函数/事件名/属性 作用
alert(字符串或变量) 警告提示框,调试频繁用
confirm(str); 确认提示框,点击确定返回true,点击取消返回false
elementNode.getAttribute(name) 通过元素节点的属性名称获取属性的值
elementNode.setAttribute(name,value) 增加一个指定名称和值的新属性,或者把一个现有的属性设定为指定的值
Object.innerHTML 用于获取或替换 HTML 元素的内容。
object.className 获取或者设置元素的类名
element.onmouseover 鼠标经过事件
element.onmouseout 鼠标移开事件
element.onfocus 聚焦事件
element.onblur 失焦事件
element.onchange 文本框内容改变
Math.ceil() 向上取整
Math.floor() 向下取整
Math.round() 四舍五入
Math.random() 生成0-1的随机数
setInterval(代码,交互时间) 从载入页面后每隔指定的时间执行代码
clearInterval(id_of_setInterval) 取消计时器(间隔执行的)
setTimeout(代码,延迟时间) 在载入后延迟指定时间后,去执行一次表达式,仅执行一次,时间单位为毫秒
clearTimeout(id_of_setTimeout) 取消计时器(一次性的)
console.log() 打印日志,还有其他等级如debug,各个等级的颜色等会不同
console.group() 日志分组,便于查看,配合console.groupend()使用,一个表示开始一个表示结束
console.dir(cmd) 列出指定命令/对象的所以方法
event.srcElement 获取当前事件源对象(固定写法,一般直接传this来替代)
elementNode.parentNode 获取父节点,父节点只有一个
elementNode.childNodes 得到全部的子节点(firstChild得到第一个,lastChile得到最后一个)
isNaN() 判断是否为数字

判断一个数是不是小数:

1
2
// 如果输入的不是数字 parse 返回 NaN
if (num == parseInt(num))

parseInt 可以强转开头为数字的字符串,字符会被忽略:parseInt ('123avc1'); 也是可以正常转换的,而使用 Number() 方法转换包含字母的字符串会报错(返回 NAN),但默认是这种转换。

最后

用到什么比较频繁的东西到时候再回来更新吧…嗯,缓慢更新
对DOM不理解的可以去谷狗下DOM事件流

喜欢就请我吃包辣条吧!

评论框加载失败,无法访问 Disqus

你可能需要魔法上网~~