jQuery性能优化
最后更新于:2022-04-01 11:44:18
[jQuery性能优化](http://www.cnblogs.com/yuzhongwusan/archive/2010/08/29/1811933.html)
现在越来越多的人应用[jQuery](http://lib.csdn.net/base/22 "jQuery知识库")了,有些同学在享受爽快淋漓coding时就将性能问题忽略了, 比如我. jquery虽在诸多的js类库中性能表现还算优秀, 但毕竟不是在用原生的[JavaScript](http://lib.csdn.net/base/18 "JavaScript知识库")开发, 性能问题还是需要引起重视的.
1. [总是从ID选择器开始继承](http://www.hemin.cn/blog/?p=1313#descend-from-an-#id)
2. [在class前使用tag](http://www.hemin.cn/blog/?p=1313#use-tags-before-classes)
3. [将jquery对象缓存起来](http://www.hemin.cn/blog/?p=1313#cache-jquery-objects)
4. [掌握强大的链式操作](http://www.hemin.cn/blog/?p=1313#harness-the-power-of-chaining)
5. [使用子查询](http://www.hemin.cn/blog/?p=1313#use-sub-queries)
6. [对直接的DOM操作进行限制](http://www.hemin.cn/blog/?p=1313#limit-direct-dom-manipulation)
7. [冒泡](http://www.hemin.cn/blog/?p=1313#leverage-event-delegation)
8. [消除无效查询](http://www.hemin.cn/blog/?p=1313#eliminate-query-waste)
9. [推迟到 $(window).load](http://www.hemin.cn/blog/?p=1313#defer-to-window-load)
10. [压缩js](http://www.hemin.cn/blog/?p=1313#compress-your-js)
11. [全面掌握jquery库](http://www.hemin.cn/blog/?p=1313#learn-the-library)
### 1\. 总是从ID选择器开始继承
在jquery中最快的选择器是ID选择器. 因为它直接来自于Javascript的getElementById()方法.
~~~
<div id="content">
<form method="post" action="/">
<h2>Traffic Light</h2>
<ul id="traffic_light">
<li><input type="radio" class="on" name="light" value="red" /> Red</li>
<li><input type="radio" class="off" name="light" value="yellow" /> Yellow</li>
<li><input type="radio" class="off" name="light" value="green" /> Green</li>
</ul>
<input class="button" id="traffic_button" type="submit" value="Go" />
</form>
</div>
~~~
像这样选择按钮是低效的:
var traffic_button = $(‘#content .button‘);
用ID直接选择按钮效率更高:
var traffic_button = $(‘#traffic_button’);
选择多个元素
提到多元素选择其实是在说DOM遍历和循环, 这些都是比较慢的东西.为了提高性能, 最好从就近的ID开始继承.
var traffic_lights = $(‘#traffic_light input’);
### 2\. 在class前使用tag
第二快的选择器是tag选择器($(‘head’)). 同理,因为它来自原生的getElementsByTagName() 方法.
~~~
<div id="content">
<form method="post" action="/">
<h2>Traffic Light</h2>
<ul id="traffic_light">
<li><input type="radio" class="on" name="light" value="red" /> Red</li>
<li><input type="radio" class="off" name="light" value="yellow" /> Yellow</li>
<li><input type="radio" class="off" name="light" value="green" /> Green</li>
</ul>
<input class="button" id="traffic_button" type="submit" value="Go" />
</form>
</div>
~~~
总是用一个tag name来限制(修饰)class (并且不要忘记就近的ID):
var active_light = $(‘#traffic_light input.on’);
注意: 在jquery中Class是最慢的选择器. IE浏览器下它会遍历所有DOM节点不管它用在那里.
不要用用tag name来修饰ID. 下面的例子将会遍历所有的div元素来查找id为’content’的哪一个节点:
var content = $(‘div#content’);
用ID修饰ID也是画蛇添足:
var traffic_light = $(‘#content #traffic_light’);
### 3\. 将jquery对象缓存起来
要养成将jquery对象缓存进变量的习惯.
永远不要这样做:
$(‘#traffic_light input.on).bind(‘click‘, function(){…});
$(‘#traffic_light input.on).css(‘border’, ’3px dashed yellow’);
$(‘#traffic_light input.on).css(‘background-color‘, ‘orange‘);
$(‘#traffic_light input.on).fadeIn(‘slow’);
最好先将对象缓存进一个变量然后再操作:
var $active_light = $(‘#traffic_light input.on’);
$active_light.bind(‘click’, function(){…});
$active_light.css(‘border’, ’3px dashed yellow’);
$active_light.css(‘background-color’, ‘orange’);
$active_light.fadeIn(‘slow’);
为了记住我们本地变量是jquery的封装, 通常用一个$作为变量前缀. 记住,永远不要让相同的选择器在你的代码里出现多次.
缓存jquery结果,备用
如果你打算将jquery结果对象用在程序的其它部分,或者你的function会多次执行, 那么就将他们缓存到一个全局变量中.
定义一个全局容器来存放jquery结果, 我们就可以在其它函数引用它们:
~~~
// 在全局范围定义一个对象 (例如: window对象)
window.$my =
{
// 初始化所有可能会不止一次要使用的查询
head : $(‘head‘),
traffic_light : $(‘#traffic_light‘),
traffic_button : $(‘#traffic_button‘)
};
function do_something()
{
//现在你可以引用存储的结果并操作它们
var script = document.createElement(‘script‘);
$my.head.append(script);
// 当你在函数内部操作是, 可以继续将查询存入全局对象中去.
$my.cool_results = $(‘#some_ul li‘);
$my.other_results = $(‘#some_table td‘);
// 将全局函数作为一个普通的jquery对象去使用.
$my.other_results.css(‘border-color‘, ‘red‘);
$my.traffic_light.css(‘border-color‘, ‘green‘);
}
~~~
### 4\. 掌握强大的链式操作
上面的例子也可以写成这样:
~~~
var $active_light = $(‘#traffic_light input.on’);$active_light.bind(‘click’, function(){…})
.css(‘border’, ’3px dashed yellow’)
.css(‘background-color’, ‘orange’)
.fadeIn(‘slow’);
~~~
这样可以写更少的代码, 让我们的js更轻量.
### 5\. 使用子查询
jQuery 允许我们对一个已包装的对象使用附加的选择器操作. 因为我们已经在保存了一个父级对象在变量里, 这样大大提高对其子元素的操作:
~~~
<div id="content">
<form method="post" action="/">
<h2>Traffic Light</h2>
<ul id="traffic_light">
<li><input type="radio" class="on" name="light" value="red" /> Red</li>
<li><input type="radio" class="off" name="light" value="yellow" /> Yellow</li>
<li><input type="radio" class="off" name="light" value="green" /> Green</li>
</ul>
<input class="button" id="traffic_button" type="submit" value="Go" />
</form>
</div>
~~~
例如, 我们可以用子查询的方法来抓取到亮或不亮的灯, 并缓存起来以备后续操作.
var $traffic_light = $(‘#traffic_light’),
$active_light = $traffic_light.find(‘input.on’),
$inactive_lights = $traffic_light.find(‘input.off’);
提示: 你可以用逗号分隔的方法一次声明多个局部变量–节省字节数
### 6\. 对直接的DOM操作进行限制
这里的基本思想是在内存中建立你确实想要的东西,然后更新DOM 。这并不是一个jQuery最佳实践,但必须进行有效的JavaScript操作 。直接的DOM操作速度很慢。
例如,你想动态的创建一组列表元素, 千万不要这么做:
~~~
var top_100_list = [...], //假设这里是100个独一无二的字符串
$mylist = $(‘#mylist‘); //jQuery 选择到 元素t
for (var i=0, l=top_100_list.length; il; i++)
{
$mylist.append(‘‘ + top_100_list[i] + ‘‘);
}
~~~
我们应该将整套元素字符串在插入进dom中之前全部创建好:
~~~
var top_100_list = [...], // assume this has 100 unique strings
$mylist = $(‘#mylist‘), // jQuery selects our element
top_100_li = ""; // 这个变量将用来存储我们的列表元素
for (var i=0, l=top_100_list.length; il; i++)
{
top_100_li += ‘‘ + top_100_list[i] + ‘‘;
}
$mylist.html(top_100_li);
~~~
我们在插入之前将多个元素包裹进一个单独的父级节点会更快:
~~~
var top_100_list = [...], // assume this has 100 unique strings
$mylist = $(‘#mylist‘), // jQuery selects our element
top_100_ul = ‘‘; // This will store our entire unordered list
for (var i=0, l=top_100_list.length; il; i++)
{
top_100_ul += ‘‘ + top_100_list[i] + ‘‘;
}
top_100_ul += ‘‘; // //关闭无序列表
$mylist.replaceWith(top_100_ul);
~~~
如果你做了以上几条还是担心有性能问题,那么:
* 试试jquery的 clone() 方法, 它会创建一个节点树的副本, 它允许以”离线”的方式进行dom操作, 当你操作完成后再将其放回到节点树里.
* 使用[DOM DocumentFragments](http://www.devguru.com/technologies/xmldom/quickref/obj_documentFragment.html). 正如jQuery作者所言, 它的性能要明显优于直接的dom操作.
### 7\. 冒泡
除非在特殊情况下, 否则每一个js事件(例如:click, mouseover, 等.)都会冒泡到父级节点. 当我们需要给多个元素调用同个函数时这点会很有用.
代替这种效率很差的多元素事件监听的方法就是, 你只需向它们的父节点绑定一次, 并且可以计算出哪个节点触发了事件.
例如, 我们要为一个拥有很多输入框的表单绑定这样的行为: 当输入框被选中时为它添加一个class
像这样绑定事件是低效的:
~~~
$(‘#entryform input).bind(‘focus‘, function(){
$(this).addClass(‘selected‘);
}).bind(‘blur‘, function(){
$(this).removeClass(‘selected‘);
});
~~~
我们需要在父级监听获取焦点和失去焦点的事件:
~~~
$(‘#entryform‘).bind(‘focus‘, function(e){
var cell = $(e.target); // e.target grabs the node that triggered the event.
cell.addClass(‘selected‘);
}).bind(‘blur‘, function(e){
var cell = $(e.target);
cell.removeClass(‘selected‘);
});
~~~
父级元素扮演了一个调度员的角色, 它可以基于目标元素绑定事件. 如果你发现你给很多元素绑定了同一个事件监听, 那么你肯定哪里做错了.
### 8\. 消除无效查询
尽管jquery可以很优雅的处理没有匹配元素的情况, 但它还是需要花费时间去寻找. 如果你整站只有一个全局js, 那么极有可能把所有的jquery函数塞进$(document)ready(function(){//所有你引以为傲的代码})里.
只运行在页面里用到的函数. 大多数有效的方法就是使用行内初始化函数, 这样你的模板就能准确的控制何时何处该执行js.
例如, 你的”文章”页面模板, 你可能会引用如下的代码在body结束处:
script type="text/javascript>
mylib.article.init();
如果你的页面模板包含一些多变的模块可能不会出现在页面中, 或者为了视觉呈现的原因你需要它们能够快速加载, 你可以将初始化函数紧跟在模块之后.
~~~
<ul id="traffic_light">
<li><input type="radio" class="on" name="light" value="red" /> Red</li>
<li><input type="radio" class="off" name="light" value="yellow" /> Yellow</li>
<li><input type="radio" class="off" name="light" value="green" /> Green</li>
</ul>
<script type="text/javascript>
mylib.traffic_light.init();
</script>
~~~
你的全局js库可能会是这样子的:
~~~
var mylib =
{
article_page :
{
init : function()
{
// Article page specific jQuery functions.
}
},
traffic_light :
{
init : function()
{
// Traffic light specific jQuery functions.
}
}
}
~~~
### 9\. 推迟到 $(window).load
jquery对于开发者来说有一个很诱人的东西, 可以把任何东西挂到$(document).ready下冒充事件. 在大多数例子中你都会发现这样的情况.
尽管$(document).rady 确实很有用, 它可以在页面渲染时,其它元素还没下载完成就执行. 如果你发现你的页面一直是载入中的状态, 很有可能就是$(document).ready函数引起的.
你可以通过将jquery函数绑定到$(window).load 事件的方法来减少页面载入时的cpu使用率. 它会在所有的html(包括iframe)被下载完成后执行.
~~~
$(window).load(function(){
// 页面完全载入后才初始化的jQuery函数.
});
~~~
多余的功能例如拖放, 视觉特效和动画, 预载入隐藏图像,等等. 都是适合这种技术的场合.
### 10\. 压缩js
推荐一个js在线压缩地址: [http://dean.edwards.name/packer/](http://dean.edwards.name/packer/)
### 11\. 全面掌握jquery库
知己知彼, 百战百胜. 只有更深入的了解jQuery才能更灵活的使用它. 这里提供一个[jQuery的速查手册](http://www.artzstudio.com/files/jquery-rules/jquery_1.3_cheatsheet_v1.pdf), 可以打印出来随身携带. 要是有能力将jQuery源码通读一遍那就更好了.
为什么说DOM操作很慢
最后更新于:2022-04-01 11:44:16
[为什么说DOM操作很慢](http://www.cnblogs.com/yuzhongwusan/articles/5275933.html)
一直都听说DOM很慢,要尽量少的去操作DOM,于是就想进一步去探究下为什么大家都会这样说,在网上学习了一些资料,这边整理出来。
首先,DOM对象本身也是一个js对象,所以严格来说,并不是操作这个对象慢,而是说操作了这个对象后,会触发一些浏览器行为,比如布局(layout)和绘制(paint)。下面主要先介绍下这些浏览器行为,阐述一个页面是怎么最终被呈现出来的,另外还会从代码的角度,来说明一些不好的实践以及一些优化方案。
##浏览器是如何呈现一张页面的
一个浏览器有许多模块,其中负责呈现页面的是渲染引擎模块,比较熟悉的有WebKit和Gecko等,这里也只会涉及这个模块的内容。
先用文字大致阐述下这个过程:
* 解析HTML,并生成一棵DOM tree
* 解析各种样式并结合DOM tree生成一棵Render tree
* 对Render tree的各个节点计算布局信息,比如box的位置与尺寸
* 根据Render tree并利用浏览器的UI层进行绘制
其中DOM tree和Render tree上的节点并非一一对应,比如一个"`display:none"`的节点就只会存在于DOM tree上,而不会出现在Render tree上,因为这个节点不需要被绘制。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-06_572c45a93efc0.jpg)
上图是Webkit的基本流程,在术语上和Gecko可能会有不同,这里贴上Gecko的流程图,不过文章下面的内容都会统一使用Webkit的术语。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-06_572c45a959d3b.jpg)
影响页面呈现的因素有许多,比如link的位置会影响首屏呈现等。但这里主要集中讨论与layout相关的内容。
paint是一个耗时的过程,然而layout是一个更耗时的过程,我们无法确定layout一定是自上而下或是自下而上进行的,甚至一次layout会牵涉到整个文档布局的重新计算。
但是layout是肯定无法避免的,所以我们主要是要最小化layout的次数。
##什么情况下浏览器会进行layout
在考虑如何最小化layout次数之前,要先了解什么时候浏览器会进行layout。
layout(reflow)一般被称为布局,这个操作是用来计算文档中元素的位置和大小,是渲染前重要的一步。在HTML第一次被加载的时候,会有一次layout之外,js脚本的执行和样式的改变同样会导致浏览器执行layout,这也是本文的主要要讨论的内容。
一般情况下,浏览器的layout是lazy的,也就是说:在js脚本执行时,是不会去更新DOM的,任何对DOM的修改都会被暂存在一个队列中,在当前js的执行上下文完成执行后,会根据这个队列中的修改,进行一次layout。
然而有时希望在js代码中立刻获取最新的DOM节点信息,浏览器就不得不提前执行layout,这是导致DOM性能问题的主因。
如下的操作会打破常规,并触发浏览器执行layout:
* 通过js获取需要计算的DOM属性
* 添加或删除DOM元素
* resize浏览器窗口大小
* 改变字体
* css伪类的激活,比如:hover
* 通过js修改DOM元素样式且该样式涉及到尺寸的改变
我们来通过一个例子直观的感受下:
~~~
// Read
var h1 = element1.clientHeight;
// Write (invalidates layout)
element1.style.height = (h1 * 2) + 'px';
// Read (triggers layout)
var h2 = element2.clientHeight;
// Write (invalidates layout)
element2.style.height = (h2 * 2) + 'px';
// Read (triggers layout)
var h3 = element3.clientHeight;
// Write (invalidates layout)
element3.style.height = (h3 * 2) + 'px';
~~~
clientHeight,这个属性是需要计算得到的,于是就会触发浏览器的一次layout。我们来利用chrome(v47.0)的开发者工具看下(截图中的timeline record已经经过筛选,仅显示layout):
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-06_572c45a976d0e.jpg)
上面的例子中,代码首先修改了一个元素的样式,接下来读取另一个元素的`clientHeight`属性,由于之前的修改导致当前DOM被标记为脏,为了保证能准确的获取这个属性,浏览器会进行一次layout(我们发现chrome的开发者工具良心的提示了我们这个性能问题)。
优化这段代码很简单,预先读取所需要的属性,在一起修改即可。
~~~
// Read
var h1 = element1.clientHeight;
var h2 = element2.clientHeight;
var h3 = element3.clientHeight;
// Write (invalidates layout)
element1.style.height = (h1 * 2) + 'px';
element2.style.height = (h2 * 2) + 'px';
element3.style.height = (h3 * 2) + 'px';
~~~
看下这次的情况:![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-06_572c45a9a6d6b.jpg)
下面再介绍一些其他的优化方案。
##最小化layout的方案
上面提到的一个批量读写是一个,主要是因为获取一个需要计算的属性值导致的,那么哪些值是需要计算的呢?
这个链接里有介绍大部分需要计算的属性:[http://gent.ilcore.com/2011/03/how-not-to-trigger-layout-in-webkit.html](http://gent.ilcore.com/2011/03/how-not-to-trigger-layout-in-webkit.html)
再来看看别的情况:
###面对一系列DOM操作
针对一系列DOM操作(DOM元素的增删改),可以有如下方案:
* documentFragment
* display: none
* cloneNode
比如(仅以documentFragment为例):
~~~
var fragment = document.createDocumentFragment();
for (var i=0; i < items.length; i++){
var item = document.createElement("li");
item.appendChild(document.createTextNode("Option " + i);
fragment.appendChild(item);
}
list.appendChild(fragment);
~~~
这类优化方案的核心思想都是相同的,就是先对一个不在Render tree上的节点进行一系列操作,再把这个节点添加回Render tree,这样无论多么复杂的DOM操作,最终都只会触发一次layout。
###面对样式的修改
针对样式的改变,我们首先需要知道并不是所有样式的修改都会触发layout,因为我们知道layout的工作是计算RenderObject的尺寸和大小信息,那么我如果只是改变一个颜色,是不会触发layout的。
这里有一个网站[CSS triggers](http://csstriggers.com/),详细列出了各个CSS属性对浏览器执行layout和paint的影响。
像下面这种情况,和上面讲优化的部分是一样的,注意下读写即可。
~~~
elem.style.height = "100px"; // mark invalidated
elem.style.width = "100px";
elem.style.marginRight = "10px";
elem.clientHeight // force layout here
~~~
但是要提一下动画,这边讲的是js动画,比如:
~~~
function animate (from, to) {
if (from === to) return
requestAnimationFrame(function () {
from += 5
element1.style.height = from + "px"
animate(from, to)
})
}
animate(100, 500)
~~~
动画的每一帧都会导致layout,这是无法避免的,但是为了减少动画带来的layout的性能损失,可以将动画元素绝对定位,这样动画元素脱离文本流,layout的计算量会减少很多。
###使用requestAnimationFrame
任何可能导致重绘的操作都应该放入`requestAnimationFrame`
在现实项目中,代码按模块划分,很难像上例那样组织批量读写。那么这时可以把写操作放在`requestAnimationFrame`的callback中,统一让写操作在下一次paint之前执行。
~~~
// Read
var h1 = element1.clientHeight;
// Write
requestAnimationFrame(function() {
element1.style.height = (h1 * 2) + 'px';
});
// Read
var h2 = element2.clientHeight;
// Write
requestAnimationFrame(function() {
element2.style.height = (h2 * 2) + 'px';
});
~~~
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-06_572c45a9d5690.jpg)
可以很清楚的观察到Animation Frame触发的时机,MDN上说是在paint之前触发,不过我估计是在js脚本交出控制权给浏览器进行DOM的invalidated check之前执行。
##其他注意点
除了由于触发了layout而导致性能问题外,这边再列出一些其他细节:
缓存选择器的结果,减少DOM查询。这里要特别提下HTMLCollection。HTMLCollection是通过`document.getElementByTagName`得到的对象类型,和数组类型很类似但是每次获取这个对象的一个属性,都相当于进行一次DOM查询:
~~~
var divs = document.getElementsByTagName("div");
for (var i = 0; i < divs.length; i++){ //infinite loop
document.body.appendChild(document.createElement("div"));
}
~~~
比如上面的这段代码会导致无限循环,所以处理HTMLCollection对象的时候要做些缓存。
另外,减少DOM元素的嵌套深度并优化css,去除无用的样式对减少layout的计算量有一定帮助。
在DOM查询时,`querySelector`和`querySelectorAll`应该是最后的选择,它们功能最强大,但执行效率很差,如果可以的话,尽量用其他方法替代。
下面两个jsperf的链接,可以对比下性能。
1)[https://jsperf.com/getelementsbyclassname-vs-queryselectorall/162](https://jsperf.com/getelementsbyclassname-vs-queryselectorall/162)
2)[http://jsperf.com/getelementbyid-vs-queryselector/218](http://jsperf.com/getelementbyid-vs-queryselector/218)
##自己对View层的想法
上面的内容理论方面的东西偏多,从实践的角度来看,上面讨论的内容,正好是View层需要处理的事情。已经有一个库FastDOM来做这个事情,不过它的代码是这样的:
~~~
fastdom.read(function() {
console.log('read');
});
fastdom.write(function() {
console.log('write');
});
~~~
问题很明显,会导致`callback hell`,并且也可以预见到像FastDOM这样的imperative的代码缺乏扩展性,关键在于用了`requestAnimationFrame`后就变成了异步编程的问题了。要让读写状态同步,那必然需要在DOM的基础上写个Wrapper来内部控制异步读写,不过都到了这份上,感觉可以考虑直接上React了......
总之,尽量注意避免上面说到的问题,但如果用库,比如[jQuery](http://lib.csdn.net/base/22 "jQuery知识库")的话,layout的问题出在库本身的抽象上。像React引入自己的组件模型,用过virtual DOM来减少DOM操作,并可以在每次state改变时仅有一次layout,我不知道内部有没有用`requestAnimationFrame`之类的,感觉要做好一个View层就挺有难度的,之后准备学学React的代码。希望自己一两年后会过来再看这个问题的时候,可以有些新的见解。
JavaScritpt的DOM初探之Node(一)
最后更新于:2022-04-01 11:44:13
DOM(文档对象模型)是针对HTML和XML文档的一个API。DOM描绘了一个层次化的节点树,允许开发人员添加,移除和修改页面的某一部分。DOM脱胎于微软公司的DHTLM(动态HTML),但是现在它已经成为表现和操作页面标记的真正跨平台,语言中立的方式。
[JavaScript](http://lib.csdn.net/base/18 "JavaScript知识库")和DOM通常被视为一个单一的实体,因为JavaScript是最常见的用于此目的(与web上的内容交互)。 使用DOM API访问、遍历和操作HTML和XML文档。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-06_572c45a8071d9.jpg)
典型的DOM的基本轮廓层次结构(简化)
这里有一个关于DOM的一些值得注意的事情:
* 窗口对象作为全局对象,您只需键入“窗口”访问它。 在这个对象的所有执行JavaScript代码。 像所有对象的属性和方法。
* 属性是一个变量存储在一个对象。 网页上创建的所有变量authomatically成为窗口对象的属性。
* 一个方法是存储在一个对象的函数。 以来所有的功能都存储在窗口对象(至少)下他们都可以称为“方法”。
* DOM创建一个hierarcy对应于每个web文档的结构。 这种层次结构是由节点。 有几种不同类型的DOM节点,最重要的是“元素”,“文本”和“文档”。
* 一个“元素”节点表示一个元素在一个页面。 如果你有一个段落元素(“”),那么它可以通过访问DOM节点。
* “文本”节点表示一个页面内所有文本(元素)。 所以如果你的段落的文本可以直接通过DOM访问。
* 文档的节点代表整个文档。 (这是DOM层次/树的根节点)。
* 还要注意,元素属性是DOM节点本身。
* 每个布局引擎实现DOM标准略有不同。 例如,[火狐](http://www.mozilla.com/en-US/firefox/)使用web浏览器[壁虎](http://en.wikipedia.org/wiki/Gecko_(layout_engine))布局引擎,有一个很好的实现(虽然不是完全内联与W3C规范)[Internet Explorer](http://www.microsoft.com/windows/products/winfamily/ie/default.mspx),它使用[三叉戟](http://en.wikipedia.org/wiki/Trident_(layout_engine))布局引擎闻名车和不完整的实现,导致web开发社区内的多痛苦!
Node类型:
dom1级定义了一个NODE接口,该接口将由DOM中的所有节点类型实现。这个Node接口在javaScript中是作为node类型实现的,除了IE之外,在其他所有浏览器中都可以访问到这个类型。javaScript中的所有节点都继承自NODE类型,因此所有节点类型都共享着相同的基本属性和方法。
每个节点都有一个nodeType属性,用于表明节点的类型。节点类型由在node类型中定义的下列12个数值常量来表示,任何节点类型必居其一:
~~~
Node.ELEMENT_NODE(1);
Node.ATTRIBUTE_NODE(2);
Node.TEXT_NODE(3);
Node.CDATA_SECTION_NODE(4);
Node.CDATA_REFERENCE_NODE(5);
Node.ENTITY_NODE(6);
Node.PROCESSING_INSTRUCTION_NODE(7);
Node.COMMENT_NODE(8);
Node.DOCUMENT_NODE(9);
NODE.DOCUMENT_TYPE_NODE(10);
Node.DOCUMENT_FRAGMENT_NODE(11);
Node.NOTATION_NODE(12)
~~~
通过比较上面这些常量,可以很容易地确定节点的类型,例如:
~~~
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=gbk">
<title>test for javascript</title>
</head>
<body>
<p id="a">漫步人生路!</p>
<p>
<script type="text/javascript">
var someNode=document.getElementById('a');
alert(someNode.nodeType);
</script>
</body>
</html>
~~~
要了解Node节点的信息可以使用nodeName和nodeValue这两个属性:
~~~
<script type="text/javascript">
var someNode=document.getElementById('a');
alert(someNode.nodeType);
alert(someNode.nodeName); //是元素的标签名 <p>
alert(someNode.nodeValue); //要坚持node是不是一个元素只要看nodeValue的值为null就可以了
</script>
~~~
JavaScript BOM
最后更新于:2022-04-01 11:44:11
**JavaScript特性:**
* 交互性
* 安全性(不可以直接访问本地硬盘)
* 跨平台性(只要是可以解析js的浏览器都可以执行,和平台无关)
**JavaScript三大核心:**
1)核心(ECMAScript):描述了JS的语法和基本对象。
2)文档对象模型 ☆(DOM):处理网页内容的方法和接口
3)浏览器对象模型(BOM):与浏览器交互的方法和接口
[![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-06_572c45a78428a.jpg)](http://www.flyne.org/wp-content/uploads/2014/08/three-parts-of-JS.jpg)
**BOM对象:**
1\. DOM 是 W3C 的标准; [所有浏览器公共遵守的标准]
2\. BOM 是 各个浏览器厂商根据 DOM
在各自浏览器上的实现;[表现为不同浏览器定义有差别,实现方式不同]
3\. window 是 BOM 对象,而非 js 对象;
**BOM常用操作:**
弹出新的浏览器窗口
移动、关闭浏览器窗口以及调整窗口大小
提供 Web 浏览器详细信息的定位对象
提供用户屏幕分辨率详细信息的屏幕对象
对 cookie 的支持
IE 扩展了 BOM,加入了 ActiveXObject 类,可以通过 JavaScript 实例化 ActiveX 对象
javacsript是通过访问BOM(Browser Object Model)对象来访问、控制、修改客户端(浏览器),由于BOM的window包含了document,window对象的属性和方法是直接可以使用而且被感知的,因此可以直接使用window对象的document属性,通过document属性就可以访问、检索、修改XHTML文档内容与结构。因为document对象又是DOM(Document Object Model)模型的根节点。可以说,BOM包含了DOM(对象),浏览器提供出来给予访问的是BOM对象,从BOM对象再访问到DOM对象,从而js可以操作浏览器以及浏览器读取到的文档。其中
DOM包含:
Window对象包含属性:document、location、navigator、screen、history、frames
Document根节点包含子节点:forms、location、anchors、images、links
从window.document已然可以看出,DOM的最根本的对象是BOM的window对象的子对象。
区别:DOM描述了处理网页内容的方法和接口,BOM描述了与浏览器进行交互的方法和接口。
window对象的全局作用域:
由于window对象同时扮演着ECMASript中Global对象的角色,因此所有在全局作用域中声明的变量,函数都会变成window对象的属性和方法。
~~~
var age=29;
function sayAge(){
alert(this.age);
}
alert(window.age); //29
sayAge(); //29
window.sayAge(); //29
~~~
全局作用域中定义了一个变量age和一个函数sayAge(),他们被自动归在了window对象的名下。于是,可以通过window.age访问变量age,可以通过window.satAge()访问函数sayAge()。由于agyAge()存在于全局作用域中,因此this.age被映射到window.age,最终显示的是正确的结果。
全局变量会成为window对象的属性,而且定义全局变量与在window对象上直接定义属性还是有一点差别:全局变量不能通过delete操作符删除,而直接在window对象上的定义的属性可以;
~~~
var age=29;
function sayAge(){
alert(this.age);
}
delete window.age;
~~~
窗口关系及框架
如果页面中包含框架,则每个框架都拥有自己的window对象,并且保存在frames集合中。在frames集合中,可以通过数值索引或者框架名称来访问对应的window对象。每个window对象都有一个name属性,其中包含框架的名称。
导航和打开窗口
使用window.open()方法既可以导航到一个特定的URL,也可以打开一个新的浏览器窗口。这个方法可以接收4个参数:需加载的URL,窗口目标,一个特性字符串以及一个标示新页面是否取代浏览器历史记录中当前加载页面的布尔值。通过只需要传递第一个参数;最后一个参数只在不打开新窗口的情况下使用。
如果window.open()传递了第二个参数,而且该参数是已有窗口或框架的名称,那么就会在其有该名称的窗口或框架中加载第一个参数指定的URL.
~~~
window.open("http://www.baidu.com",'百度');
~~~
**1.弹出窗口**
如果给window.open()传递的第二个参数并不是一个已经存在的窗口或者框架,那么该方法就会根据在第三个参数位置传入的字符串创建一个新窗口或者标签页。如果没有传入第三个参数,那么就会打开一个带有全部默认设置的新窗口;具体弹出窗口后面详解
**2.安全限制**
现在很多浏览器都对弹出窗口做了很多安全限制,包括不允许再屏幕之外创建弹出窗口,不允许将弹出窗口移到屏幕以外,不允许关闭状态栏等。不允许关闭地址栏。
**3.弹出窗口屏蔽程序**
大多数浏览器都内置有弹出窗口屏蔽程序,而没有内置该类程序的浏览器,也可以安装在YAhoo等带有内容中屏蔽程序的使用工具,下面的例子可以检测弹出窗口是否被屏蔽,如果屏蔽的话会返回null;
~~~
var name=window.open("http://www.baidu.com",'百度');
alert(name);
~~~
JavaScript的闭包理解
最后更新于:2022-04-01 11:44:09
由于本人是做[Java Web](http://lib.csdn.net/base/13 "Java Web知识库") 开发的,对js只是存在很浅的理解,js闭包的概念很早就听说了,但是一直都不明白是什么意思,今天准备梳理一下闭关的概念;
闭包(`closure`)是`[JavaScript](http://lib.csdn.net/base/18 "JavaScript知识库")`语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现。
闭包的特性:
1.函数嵌套函数
2.函数内部可以引用外部的参数和变量
3.参数和变量不会被垃圾回收机制回收
## 闭包的定义及其优缺点
闭包是指有权访问另一个函数作用域中的变量的函数,创建闭包的最常见的方式就是在一个函数内创建另一个函数,通过另一个函数访问这个函数的局部变量
闭包的缺点就是常驻内存,会增大内存使用量,使用不当很容易造成内存泄露。
闭包是`javascript`语言的一大特点,主要应用闭包场合主要是为了:设计私有的方法和变量。
一般函数执行完毕后,局部活动对象就被销毁,内存中仅仅保存全局作用域。但闭包的情况不同!
嵌套函数的闭包:
~~~
function sum(){
var a=1;
return function(){
alert(a++)
};
}
var fun=sum();
fun(); //执行后a++,然后a还在
fun();
fun=null; //a被回收
~~~
闭包会使变量始终保存在内存中,如果不当使用会增大内存消耗。
##javascript的垃圾回收原理
(1)、在`javascript`中,如果一个对象不再被引用,那么这个对象就会被`GC`回收;
(2)、如果两个对象互相引用,而不再被第`3`者所引用,那么这两个互相引用的对象也会被回收。
使用闭包的好处
那么使用闭包有什么好处呢?使用闭包的好处是:
~~~
全选复制放进笔记<script>
var abc = (function(){ //abc为外部匿名函数的返回值
var a = 1;
return function(){
a++;
alert(a);
}
})();
abc(); //2 ;调用一次abc函数,其实是调用里面内部函数的返回值
abc(); //3
</script>
~~~
## 五、私有成员的存在
~~~
var aaa=(function(){
var a=1;
function bbb(){
a++;
return a;
}
function ccc(){
a++;
return a;
}
return {
b:bbb, //为内部方法取一个别名 json结构
c:ccc
}
})();
alert(aaa.b()); //2
alert(aaa.c()); //3
~~~
## 六.使用匿名函数实现累加
我们先看看不使用匿名函数的情况下
~~~
function box(){
var age=100;
age++;
return age;
}
var b=box();
alert(b);
~~~
在看看使用匿名函数的情况
~~~
//使用匿名函数实现局部变量驻留内存中,从而实现累加
<script type="text/javascript">
function box(){
var age = 100;
return function(){ //匿名函数
age++;
return age;
};
}
var b = box();
alert(b());
~~~
过度使用闭包会导致性能的下降。函数里放匿名函数,则产生了闭包.在这种情况下我们最好不要使用闭包;如果使用了闭包要记得释放引用;
~~~
b = null; //解除引用,等待垃圾回收
~~~
## 七、在循环中直接找到对应元素的索引
~~~
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
<title></title>
<script>
window.onload = function(){
var aLi = document.getElementsByTagName('li');
for (var i=0;i<aLi.length;i++){
(function(i){
aLi[i].onclick = function(){
alert(i);
};
})(i);
}
};
</script>
</head>
<body>
<ul>
<li>123</li>
<li>456</li>
<li>789</li>
</ul>
</body>
</html>
~~~
以上内容转载自https://segmentfault.com/a/1190000000652891
闭包的深入理解:
闭包和匿名函数的两个概念,上面说过闭包是指有权访问另一个函数作用域中的变量的函数。创建闭包最简单的方式,就是在一个函数内部创建一个匿名函数。
闭包与变量:
作用域链的这种配置机制引出了一个值得注意的副作用,即闭包只能取得包含函数中任何变量的最后一个值。闭包所保存的是整个变量对象,而不是某个特殊的变量。
~~~
function createArray(){
var array=new Array();
for(var i=0;i<10;i++){
array[i]=function(){
return i;
};
}
return array;
}
createArray();
~~~
这个函数会返回一个函数数值。表面上看,似乎每个函数都应该返回自己的索引值,即位置0的函数返回0,位置1的返回1.单实际上每个函数都返回10.因为每个函数的作用域链中都保存着这个函数的活动对象。所以他们引用的都是同一个变量i。当createArray创建后,变量i的值是10,此时每个函数都引用着保存变量i的同一个变量对象,所以在每个函数内部i的值都是10.我们可以通过匿名函数来让闭包的行为符合预期
~~~
function createArray(){
var array=new Array();
for(var i=0;i<10;i++){
array[i]=function(num){
return function(){
return num
}
}(i);
}
return array;
}
~~~
在重写了前面的函数后,每个函数就会返回各自不同的索引值了。在这个版本中,我们没有直接把闭包赋值给数值,而是定义了一个匿名函数,并将立即执行该匿名函数的结果赋值给数组。这里的匿名函数有一个参数num,也就是最终的函数要返回的值。在调用每个匿名函数时。我们传入了变量i.由于该函数时按值传递的,所以就会将i的当前值复制给参数num。而在这个匿名函数内部,又创建并返回了一个访问num的闭包。这样一来,result数组中的每个函数都有自己num变量的一个副本,因为就可以返回各自不同的数值了;
JavaScript函数表达式
最后更新于:2022-04-01 11:44:07
在ECMAScript中,有两个最常用的创建函数对象的方法,即使用函数表达式或者使用函数声明。对此,ECMAScript规范明确了一点,即是,即函数声明 必须始终带有一个标识符(Identifier),也就是我们所说的函数名,而函数表达式则可以省略。
**函数声明的语法是这样的:**
~~~
<script>
function functionName(){
函数体
}
</script>
~~~
首先是function关键字,这个关键字表示我声明的是一个函数,或者说我将要声明一个函数了,告诉浏览器一下;然后是函数的名字,我们讲函数的名字一定要见名知义,接着里面是我们的函数体,也可以了解为函数表达式;在各大主流浏览器里面都给函数定义了一个非标准的name属性,通过这个属性可以访问到给函数指定的名字,这个属性的值永远等同于function后面的标识符;
~~~
function functionName(){
}
alert(functionName.name);
~~~
函数声明解析过程如下:
1\. 创建一个new Function对象,FormalParameterList指定参数,FunctionBody指定函数体。将当前正在运行环境中作用域链作为它的作用域。
2\. 为当前变量对象创建一个名为Identifier的属性,值为Result(1)。
**函数声明提升**
关于函数有一个重要的特性就是函数声明提升.意思是在读取代码前会先读取函数声明。这就意味着可以把函数声明放在调用它额语句后面;例如一下;
~~~
alert(functionName.name);
function functionName(){
}
~~~
这个例子不会抛出错误,因为在alert之前会先去读取这个函数声明;
函数表达式
第二种创建函数的方法是使用函数表达式:
函数表达式:
(函数表达式分为匿名和具名函数表达式)
function Identifier opt( FormalParameterList opt){ FunctionBody } //这里是具名函数表达式
具名函数表达式的解析过程如下:
1\. 创建一个new Object对象
2\. 将Result(1)添加到作用域链的顶端
3\. 创建一个new Function对象,FormalParameterList指定参数,FunctionBody指定函数体。将当前正在运行的执行环境中作用域链作为它的作用域。
4\. 为Result(1)创建一个名为Identifier 的属性,其值为为Result(3),只读,不可删除
5\. 从作用域链中移除Result(1)
6\. 返回Result(3)
简单来说,ECMAScript是通过上下文来区分这两者的:假如 function foo(){} 是一个赋值表达式的一部分,则认为它是一个函数表达式。而如果 function foo(){} 被包含在一个函数体内,或者位于程序(的最上层)中,则将它作为一个函数声明来解析。显然,在省略标识符的情况下,“表达式” 也就只能是表达式了。
~~~
function foo(){}; // 声明,因为它是程序的一部分
var bar = function foo(){}; // 表达式,因为它是赋值表达(AssignmentExpression)的一部分
new function bar(){}; // 表达式,因为它是New表达式(NewExpression)的一部分
(function(){
function bar(){}; // 声明,因为它是函数体(FunctionBody)的一部分
})();
~~~
还有一种情况:
~~~
(function foo(){})
这种情况也是函数表达式,它被包含在一对圆括号中的函数,在其上下文环境中,()构成了一个分组操作符,而分组操作符只能包含表达式,更多的例子:
function foo(){}; // 函数声明
(function foo(){}); // 函数表达式:注意它被包含在分组操作符中
try {
(var x = 5); // 分组操作符只能包含表达式,不能包含语句(这里的var就是语句)
}
catch(err) {
// SyntaxError(因为“var x = 5”是一个语句,而不是表达式——对表达式求值必须返回值,但对语句求值则未必返回值。——译
}
~~~
下面简单说说函数声明与函数表达式的异同。声明和表达式的行为存在着十分微妙而又十分重要的差别。
首先,函数声明会在任何表达式被解析和求值之前先行被解析和求值。即使声明位于源代码中的最后一行,它也会先于同一作用域中位于最前面的表达式被求值。
简单总结,区别在什么地方呢?
1\. 声明总是在作用域开始时先行解析;
2\. 表达式在遇到时候才运算。
函数声明还有另外一个重要的特点,即通过条件语句控制函数声明的行为并未标准化,因此不同环境下可能会得到不同的结果。即是:
// 千万不要这样做!
// 不同[浏览器](http://www.2cto.com/os/liulanqi/)会有不同返回结果,
~~~
if (true) {
function foo() {
return 'first';
}
}
else {
function foo() {
return 'second';
}
}
foo();
~~~
// 记住,这种情况下要使用函数表达式:
~~~
var foo;
if (true) {
foo = function() {
return 'first';
};
}
else {
foo = function() {
return 'second';
};
}
foo();
~~~
那么,使用函数声明的实际规则到底是什么?
FunctionDeclaration(函数声明)只能出现在Program(程序)或FunctionBody(函数体)内。从句法上讲,它们 不能出现在Block(块)({ ... })中,例如不能出现在 if、while 或 for 语句中。因为 Block(块) 中只能包含Statement(语句), 而不能包含FunctionDeclaration(函数声明)这样的SourceElement(源元素)。
另一方面,仔细看一看产生规则也会发现,唯一可能让Expression(表达式)出现在Block(块)中情形,就是让它作为ExpressionStatement(表达式语句)的一部分。但是,规范明确规定了ExpressionStatement(表达式语句)不能以关键字function开头。而这实际上就是说,FunctionExpression(函数表达式)同样也不能出现在Statement(语句)或Block(块)中(别忘了Block(块)就是由Statement(语句)构成的)。
由于存在上述限制,只要函数出现在块中(像上面例子中那样),实际上就应该将其看作一个语法错误,而不是什么函数声明或表达式。
那么我们应该在什么时候使用函数声明或函数表达式呢?函数声明只能出现在“程序代码”中,意味着只能在其它函数体中或者全局空间;它们的定义不能不能赋值给一个变量或属性,或者作为一个参数传递出现在函数调用中;下面的例子是函数声明的允许的用法,foo(),bar()和local()都是通过函数声明模式声明:
// 全局环境
~~~
function foo() {}
function local() {
// 局部环境
function bar() {}
return bar;
}
~~~
当你在语法上不能使用函数声明的时候,你就可以使用函数表达式。比如:传递一个函数作为参数或者在对象字面量中定义一个函数:
// 这是一个匿名函数表达式
~~~
callMe(function () {
//传递一个函数作为参数
});
~~~
// **其他函数表达式**
~~~
var myobject = {
say: function () {
// I am a function expression
}
};
~~~
这种情况看起来好像是常规的变量赋值语句,即创建一个函数并将它复制给变量functionName.这种情况下创建的函数叫匿名函数。因为function关键字后面没有标识符。匿名函数的name属性是空字符串。函数表达式与其他表达式一样,在使用前必须先复制。而且函数表达式并不会函数生, 提升,即先执行函数表达式,再声明会报错;
[JavaScript](http://lib.csdn.net/base/18 "JavaScript知识库")有很多有趣的用法,在[Google Code Search](http://www.google.com/codesearch?as_q=%22~function%22&as_lang=javascript)里能找到不少,举一个例子:
~~~
<script>
~function() {
alert("hello, world.");
}();
</script>
~~~
试一下就知道这段代码的意思就是声明一个函数,然后立刻执行,因为Javascript中的变量作用域是基于函数的,所以这样可以避免变量污染,但这里的位运算符『~』乍一看让人摸不到头脑,如果去掉它再运行则会报错:SyntaxError。
为什么去掉位操作符『~』后运行会报错,这是因为从语法解析的角度看,Javascript不允许在函数声明的后面直接使用小括号,而函数表达式则没有这个限制,通过在函数声明前面加上一个『~』操作符,就可以让语法解析器把后面看成是函数表达式,同样的,在函数声明前面加上『!,+,-』等操作符也是可行的。
那我们为什么不使用下面这种函数表达式的方式呢?
~~~
<script>
var foo = function() {
alert("hello, world.");
}();
</script>
~~~
虽然从语法解析的角度看没有问题,但是上面的代码存在弊端,它引入了一个变量,可能会污染现有的运行环境,带来潜在的问题。
使用位操作符“~”的方法显得有点奇技淫巧,其实把函数声明用小括号套起来更易读:
~~~
<script>
(function() {
alert("hello, world.");
})();
</script>
~~~
Jquery和angularjs获取check框选中的值小技巧
最后更新于:2022-04-01 11:44:04
在我们平常的开发中,有时候会需要获取一下check框选中的值,以及check框选中行的所有信息。这个时候有一个小技巧那就是我们可以把要获取的信息全部放到check框的值里面,这样我们可以获取check框选中值的时候也就相当于获取了当前行的信息。
~~~
<td>
<input class="states" type="checkbox" name="orders" value="{{e.merchantId}},{{e.orderCode}},{{e.userId}}"/>
</td>
~~~
**全选全不选:**
~~~
var bischecked=$('#cboxchecked').is(':checked');
var fruit=$('input[name="orders"]');
fruit.prop('checked',bischecked);
~~~
这里为什么要用prop而不用attr,这是因为
- 对于HTML元素本身就带有的固有属性,在处理时,使用prop方法。
- 对于HTML元素我们自己自定义的DOM属性,在处理时,使用attr方法。
**获取选中的值:**
~~~
$('input[name="orders"]:checked').each(function(){
var sfruit=$(this).val();
var orders=sfruit.split(",");
var reminder=new Object();
reminder.merchantId=orders[0];
reminder.orderCode=orders[1];
reminder.userId=orders[2];
});
~~~
**angularjs实现:**
使用angularjs我们不用去操作dom,我们只需要去关心这个值的状态;
首先看一下html代码:
~~~
<!DOCTYPE html>
<html data-ng-app="App">
<head>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.8/angular.min.js"></script>
<script src="script2.js"></script>
</head>
<body data-ng-controller="AddStyleCtrl">
<div>Choose Tags</div>
<div>
<div>You have choosen:</div>
<hr>
<label data-ng-repeat="selectedTag in selectedTags">
(({{selectedTag}}))
</label>
<hr>
<div data-ng-repeat="category in tagcategories">
<div>{{ category.name }}</div>
<div data-ng-repeat="tag in category.tags">
<div>
<input type="checkbox" id={{tag.id}} name="{{tag.name}}" ng-checked="isSelected(tag.id)" ng-click="updateSelection($event,tag.id)">
{{ tag.name }}
</div>
</div>
<hr>
</div>
</div>
<pre>{{selected|json}}</pre>
<pre>{{selectedTags|json}}</pre>
</body>
</html>
~~~
line2 定义了AngularJS App;
line4 引入angularjs脚本;
line5 引入自己写的script2.js脚本;
line7 指定控制器AddStyleCtrl
line13-15 实时显示已选标签给用户看;
line17-line26 使用双重循环列出数据库(本例中就存储在了controller的一个对象里)中的数据;
line21的这行代码作用可大了:<inputtype="checkbox" id={{tag.id}}name="{{tag.name}}" ng-checked="isSelected(tag.id)" ng-click="updateSelection($event,tag.id)">
存储了tag的id,name,利用isSelected(tag.id)来判断是否被checked,点击时候调用updateSelection($event,tag.id)方法;
如果想 ng-click 触发的函数里获取到该触发该函数的元素不能直接传入 this ,而需要传入event。因为在Angularjs里面,这个地方的this是 scope 。我们可以传入 event,然后在函数里面通过 event.target 来获取到该元素。
line29-30 是测试时候给自己看的,可以看到selected数组和selectedTags数组中的内容;
然后看看AngularJS代码:(script2.js)
~~~
/**
* Created by zh on 20/05/15.
*/
// Code goes here
var iApp = angular.module("App", []);
iApp.controller('AddStyleCtrl', function($scope)
{
$scope.tagcategories = [
{
id: 1,
name: 'Color',
tags: [
{
id:1,
name:'color1'
},
{
id:2,
name:'color2'
},
{
id:3,
name:'color3'
},
{
id:4,
name:'color4'
},
]
},
{
id:2,
name:'Cat',
tags:[
{
id:5,
name:'cat1'
},
{
id:6,
name:'cat2'
},
]
},
{
id:3,
name:'Scenario',
tags:[
{
id:7,
name:'Home'
},
{
id:8,
name:'Work'
},
]
}
];
$scope.selected = [];
$scope.selectedTags = [];
var updateSelected = function(action,id,name){
if(action == 'add' && $scope.selected.indexOf(id) == -1){
$scope.selected.push(id);
$scope.selectedTags.push(name);
}
if(action == 'remove' && $scope.selected.indexOf(id)!=-1){
var idx = $scope.selected.indexOf(id);
$scope.selected.splice(idx,1);
$scope.selectedTags.splice(idx,1);
}
}
$scope.updateSelection = function($event, id){
var checkbox = $event.target;
var action = (checkbox.checked?'add':'remove');
updateSelected(action,id,checkbox.name);
}
$scope.isSelected = function(id){
return $scope.selected.indexOf(id)>=0;
}
});
~~~
JavaScript运行原理解析
最后更新于:2022-04-01 11:44:02
**写在前面的话:**
发现使用了那么长时间的Javascript,但是对其运行原理还是不清晰,今天特意总结一下,把大神们的理论和自己的总结都记录到下面;
**1. 什么是JavaScript解析引擎?**
简单地说,JavaScript解析引擎就是能够“读懂”JavaScript代码,并准确地给出代码运行结果的一段程序。比方说,当你写了 *var a = 1 + 1;* 这样一段代码,JavaScript引擎做的事情就是看懂(解析)你这段代码,并且将a的值变为2。
学过编译原理的人都知道,对于静态语言来说(如Java、C++、C),处理上述这些事情的叫编译器(Compiler),相应地对于JavaScript这样的动态语言则叫解释器(Interpreter)。这两者的区别用一句话来概括就是:编译器是将源代码编译为另外一种代码(比如机器码,或者字节码),而解释器是直接解析并将代码运行结果输出。 比方说,firebug的console就是一个JavaScript的解释器。
但是,现在很难去界定说,JavaScript引擎它到底算是个解释器还是个编译器,因为,比如像V8(Chrome的JS引擎),它其实为了提高JS的运行性能,在运行之前会先将JS编译为本地的机器码(native machine code),然后再去执行机器码(这样速度就快很多),相信大家对JIT(Just In Time Compilation)一定不陌生吧。
我个人认为,不需要过分去强调JavaScript解析引擎到底是什么,了解它究竟做了什么事情我个人认为就可以了。对于编译器或者解释器究竟是如何看懂代码的,翻出大学编译课的教材就可以了。这里还要强调的就是,JavaScript引擎本身也是程序,代码编写而成。比如V8就是用C/C++写的。
**2. JavaScript解析引擎与ECMAScript是什么关系?**
JavaScript引擎是一段程序,我们写的JavaScript代码也是程序,如何让程序去读懂程序呢?这就需要定义规则。比如,之前提到的*var a = 1 + 1;*,它表示:
- 左边var代表了这是申明(declaration),它申明了a这个变量
- 右边的+表示要将1和1做加法
- 中间的等号表示了这是个赋值语句
- 最后的分号表示这句语句结束了
上述这些就是规则,有了它就等于有了衡量的标准,JavaScript引擎就可以根据这个标准去解析JavaScript代码了。那么这里的ECMAScript就是定义了这些规则。其中ECMAScript 262这份文档,就是对JavaScript这门语言定义了一整套完整的标准。其中包括:
- var,if,else,break,continue等是JavaScript的关键词
- abstract,int,long等是JavaScript保留词
- 怎么样算是数字、怎么样算是字符串等等
- 定义了操作符(+,-,>,<等)
- 定义了JavaScript的语法
- 定义了对表达式,语句等标准的处理算法,比如遇到==该如何处理
- ⋯⋯
标准的JavaScript引擎就会根据这套文档去实现,注意这里强调了标准,因为也有不按照标准来实现的,比如IE的JS引擎。这也是为什么JavaScript会有兼容性的问题。至于为什么IE的JS引擎不按照标准来实现,就要说到浏览器大战了,这里就不赘述了,自行Google之。
所以,简单的说,ECMAScript定义了语言的标准,JavaScript引擎根据它来实现,这就是两者的关系。
**3. JavaScript解析引擎与浏览器又是什么关系?**
简单地说,JavaScript引擎是浏览器的组成部分之一。因为浏览器还要做很多别的事情,比如解析页面、渲染页面、Cookie管理、历史记录等等。那么,既然是组成部分,因此一般情况下JavaScript引擎都是浏览器开发商自行开发的。比如:IE9的Chakra、Firefox的TraceMonkey、Chrome的V8等等。
从而也看出,不同浏览器都采用了不同的JavaScript引擎。因此,我们只能说要深入了解哪个JavaScript引擎。
**4.为什么JavaScript是单线程?**
JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。那么,为什么JavaScript不能有多个线程呢?这样能提高效率啊。
JavaScript的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?
所以,为了避免复杂性,从一诞生,JavaScript就是单线程,这已经成了这门语言的核心特征,将来也不会改变。
为了利用多核CPU的计算能力,HTML5提出Web Worker标准,允许JavaScript脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM。所以,这个新标准并没有改变JavaScript单线程的本质。
## 二、任务队列
单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。
如果排队是因为计算量大,CPU忙不过来,倒也算了,但是很多时候CPU是闲着的,因为IO设备(输入输出设备)很慢(比如Ajax操作从网络读取数据),不得不等着结果出来,再往下执行。
JavaScript语言的设计者意识到,这时主线程完全可以不管IO设备,挂起处于等待中的任务,先运行排在后面的任务。等到IO设备返回了结果,再回过头,把挂起的任务继续执行下去。
于是,所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
具体来说,异步执行的运行机制如下。(同步执行也是如此,因为它可以被视为没有异步任务的异步执行。)
(1)所有同步任务都在主线程上执行,形成一个[执行栈](http://www.ruanyifeng.com/blog/2013/11/stack.html)(execution context stack)。
(2)主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
(3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
(4)主线程不断重复上面的第三步。
![任务队列](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-06_572c45a4e40ed.jpg)
##三、事件和回调函数
"任务队列"是一个事件的队列(也可以理解成消息的队列),IO设备完成一项任务,就在"任务队列"中添加一个事件,表示相关的异步任务可以进入"执行栈"了。主线程读取"任务队列",就是读取里面有哪些事件。
"任务队列"中的事件,除了IO设备的事件以外,还包括一些用户产生的事件(比如鼠标点击、页面滚动等等)。只要指定过回调函数,这些事件发生时就会进入"任务队列",等待主线程读取。
所谓"回调函数"(callback),就是那些会被主线程挂起来的代码。异步任务必须指定回调函数,当主线程开始执行异步任务,就是执行对应的回调函数。
"任务队列"是一个先进先出的数据结构,排在前面的事件,优先被主线程读取。主线程的读取过程基本上是自动的,只要执行栈一清空,"任务队列"上第一位的事件就自动进入主线程。但是,由于存在后文提到的"定时器"功能,主线程首先要检查一下执行时间,某些事件只有到了规定的时间,才能返回主线程。
##四、Event Loop
主线程从"任务队列"中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环)。
为了更好地理解Event Loop,请看下图(转引自Philip Roberts的演讲[《Help, I'm stuck in an event-loop》](http://vimeo.com/96425312))。
![Event Loop](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-06_572c45a5063bc.png "")
上图中,主线程运行的时候,产生堆(heap)和栈(stack),栈中的代码调用各种外部API,它们在"任务队列"中加入各种事件(click,load,done)。只要栈中的代码执行完毕,主线程就会去读取"任务队列",依次执行那些事件所对应的回调函数。
执行栈中的代码(同步任务),总是在读取"任务队列"(异步任务)之前执行。请看下面这个例子。
上图中,主线程运行的时候,产生堆(heap)和栈(stack),栈中的代码调用各种外部API,它们在"任务队列"中加入各种事件(click,load,done)。只要栈中的代码执行完毕,主线程就会去读取"任务队列",依次执行那些事件所对应的回调函数。
执行栈中的代码(同步任务),总是在读取"任务队列"(异步任务)之前执行。请看下面这个例子。
~~~
var req = new XMLHttpRequest();
req.open('GET', url);
req.onload = function (){};
req.onerror = function (){};
req.send();
~~~
上面代码中的req.send方法是Ajax操作向服务器发送数据,它是一个异步任务,意味着只有当前脚本的所有代码执行完,系统才会去读取"任务队列"。所以,它与下面的写法等价。
~~~
var req = new XMLHttpRequest();
req.open('GET', url);
req.send();
req.onload = function (){};
req.onerror = function (){};
~~~
也就是说,指定回调函数的部分(onload和onerror),在send()方法的前面或后面无关紧要,因为它们属于执行栈的一部分,系统总是执行完它们,才会去读取"任务队列"。
##五、定时器
除了放置异步任务的事件,"任务队列"还可以放置定时事件,即指定某些代码在多少时间之后执行。这叫做"定时器"(timer)功能,也就是定时执行的代码。
定时器功能主要由setTimeout()和setInterval()这两个函数来完成,它们的内部运行机制完全一样,区别在于前者指定的代码是一次性执行,后者则为反复执行。以下主要讨论setTimeout()。
setTimeout()接受两个参数,第一个是回调函数,第二个是推迟执行的毫秒数。
~~~
console.log(1);
setTimeout(function(){console.log(2);},1000);
console.log(3);
~~~
上面代码的执行结果是1,3,2,因为setTimeout()将第二行推迟到1000毫秒之后执行。
如果将setTimeout()的第二个参数设为0,就表示当前代码执行完(执行栈清空)以后,立即执行(0毫秒间隔)指定的回调函数。
~~~
setTimeout(function(){console.log(1);}, 0);
console.log(2);
~~~
上面代码的执行结果总是2,1,因为只有在执行完第二行以后,系统才会去执行"任务队列"中的回调函数。
总之,setTimeout(fn,0)的含义是,指定某个任务在主线程最早可得的空闲时间执行,也就是说,尽可能早得执行。它在"任务队列"的尾部添加一个事件,因此要等到同步任务和"任务队列"现有的事件都处理完,才会得到执行。
HTML5标准规定了setTimeout()的第二个参数的最小值(最短间隔),不得低于4毫秒,如果低于这个值,就会自动增加。在此之前,老版本的浏览器都将最短间隔设为10毫秒。另外,对于那些DOM的变动(尤其是涉及页面重新渲染的部分),通常不会立即执行,而是每16毫秒执行一次。这时使用requestAnimationFrame()的效果要好于setTimeout()。
需要注意的是,setTimeout()只是将事件插入了"任务队列",必须等到当前代码(执行栈)执行完,主线程才会去执行它指定的回调函数。要是当前代码耗时很长,有可能要等很久,所以并没有办法保证,回调函数一定会在setTimeout()指定的时间执行。
总结:
以前只是认为javaScript只是一种简单的脚本语言而已,但是随着深入之后慢慢的发现,javaScript还是很复杂的,知识点还是挺多的,JS执行原理大致上已经了解了,但是如果想要深入的话,还需要多努力,多看点书,高效能的javaScript和javaScript高级程序设计都还不错.
JavaScript数组
最后更新于:2022-04-01 11:44:00
**何为数组:**
所谓数组,就是相同数据类型的元素按一定顺序排列的[集合](http://baike.baidu.com/subview/15216/10703233.htm),就是把有限个类型相同的变量用一个名字命名,然后用编号区分他们的变量的集合,这个名字称为数组名,编号称为[下标](http://baike.baidu.com/view/729047.htm)。组成数组的各个变量称为数组的分量,也称为数组的元素,有时也称为下标变量。数组是在程序设计中,为了处理方便, 把具有相同类型的若干变量按有序的形式组织起来的一种形式。这些按序排列的同类数据元素的集合称为数组。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-06_572c45a383922.jpg)
在JS中除了Object类型以为,我们用的最多的应该就是数组了。而且JS数组和其他大多数语言中的数组有着很大的区别。在Java语言中,我们声明了一个数组,那么这个数组只能保存相同类型的值,集合数组也是一样;而在我们的JS数组中的每一项都可以保存任何数据,也就是说我们的JS数组第一个位置可以用来保存字符串,第二个位置可以用来保存数值,第三个位置可以用来保存对象。并且JS的数组是动态调整的,有点类似于Java的集合数组(List);会随着数据的增加而扩大数组的容量;
**1、数组的创建**
~~~
var array = new Array(); //创建一个数组
<span style="white-space:pre"> </span>var array = new Array([2]); //创建一个数组并指定长度,注意不是上限,是长度
var array = new Array([1,'字符串',new Object()]); //创建一个数组并赋值,对应的是数字,字符串,对象
alert(array);
~~~
**2.检测数组**
~~~
array instanceof Array; //返回结果为布尔值
Array.isArray(array); //返回结果为布尔值,只支持IE9,谷歌,火狐能
~~~
**3.数组的转换**
~~~
var person1={
toLocaleString :function(){
return "Nikolaos";
},
toString:function(){
return "NiCholas";
}
};
var person2= {
toLocaleString :function(){
return "Grigorios";
},
toString:function(){
return "Greg";
}
};
var people=[person1,person2];
alert(people);
alert(people.toString());
alert(people.toLocaleString());
~~~
数组转换成字符串可以使用toStirng,valueOf,toLocaleString,Join;
**4操作方法**
**4.1数组的元素的访问**
| `1` | `var` `testGetArrValue=arrayObj[1]; ``//获取数组的元素值` |
|-----|-----|
| `2` | |
|-----|-----|
| `3` | `arrayObj[1]= ``"这是新值"``; ``//给数组元素赋予新的值` |
|-----|-----|
**4.2数组元素的添加**
| `1` | `arrayObj. push([item1 [item2 [. . . [itemN ]]]]);``// 将一个或多个新元素添加到数组结尾,并返回数组新长度` |
|-----|-----|
| `2` | |
|-----|-----|
| `3` | `arrayObj.unshift([item1 [item2 [. . . [itemN ]]]]);``// 将一个或多个新元素添加到数组开始,数组中的元素自动后移,返回数组新长度` |
|-----|-----|
| `4` | |
|-----|-----|
| `5` | `arrayObj.splice(insertPos,0,[item1[, item2[, . . . [,itemN]]]]);``//将一个或多个新元素插入到数组的指定位置,插入位置的元素自动后移,返回""。` |
|-----|-----|
**4.3数组元素的删除**
| `1` | `arrayObj.pop(); ``//移除最后一个元素并返回该元素值` |
|-----|-----|
| `2` | |
|-----|-----|
| `3` | `arrayObj.shift(); ``//移除最前一个元素并返回该元素值,数组中元素自动前移` |
|-----|-----|
| `4` | |
|-----|-----|
| `5` | `arrayObj.splice(deletePos,deleteCount); ``//删除从指定位置deletePos开始的指定数量deleteCount的元素,数组形式返回所移除的元素` |
|-----|-----|
**4.4数组的截取和合并**
| `1` | `arrayObj.slice(start, [end]); ``//以数组的形式返回数组的一部分,注意不包括 end 对应的元素,如果省略 end 将复制 start 之后的所有元素` |
|-----|-----|
| `2` | |
|-----|-----|
| `3` | `arrayObj.concat([item1[, item2[, . . . [,itemN]]]]); ``//将多个数组(也可以是字符串,或者是数组和字符串的混合)连接为一个数组,返回连接好的新的数组` |
|-----|-----|
**5.栈方法**
JS数组也提供了一种让数组的行为类似于其他数据结构的方法,数组表现的就像栈一样。栈是一种可以限制插入和删除项,后进先出的数据结构,也就是最新添加的项最早被移除。而栈中项的插入(推入)和移除(弹出)。只发生在栈顶。JS为数组专门提供了push()和pop()方法,以便实现类似栈的行为;
~~~
var names=["张三","李四"];
names.push("王五"); //添加另一项
names[3]="赵六" //添加一项
alert(names.length); //4
var item=names.pop(); //去的最后一项
alert(item);
~~~
**6.队列方法**
栈数据结构的访问规则是后进先出,而队列的数据结构的访问规则是先进先出。队列在列表的末端添加项,从列表的前端移除项。
~~~
var names=["张三","李四"];
names.push("王五"); //添加另一项
names[3]="赵六" //添加一项
alert(names.length); //4
var item=names.shift(); //取得第一项
alert(item);
~~~
**7.重排序方法**
~~~
var values=[4,2,5,7,1];
alert(values.sort()); //sort正序
alert(values.reverse()); //reverse倒序
~~~
**8.位置方法**
位置方法splice()方法,这个方法是最强大的数组方法了,它有很多种用法。splice()的主要用途是向数组的中部插入项,但使用这种方法的方式有三种;
删除:可以删除任意数量的项,只需指定2个参数:要删除的起始位置和要删除的项数,例如
splice(0,2)会删除数组中的前两项
插入:可以向指定位置插入任意数量的项,只需提供3个参数,起始位置0(要删除的项数)和要插入的项,如果要插入多个项,可以再传入第四,第五,以致任意多个项,例如splice(2,0,"张三三","李四四")会从当前数组的位置2开始插入
替换:可以向指定位置插入任意数量的项,且同时删除任意数量的项,只需指定3个参数:起始位置,要删除的项数和要插入的任意数量的项,插入的项数不必与删除的项数相等。例如splice(2,1,"张三三","李四四")会删除当前数组位置2的项,然后再从位置2开始插入字符串;
后面还有更深入的迭代和缩小方法,留着下次讨论;
搞对象前,你得先有对象
最后更新于:2022-04-01 11:43:57
我们在生活中经常听到对象两个词,那么对象到底是个 什么玩意啦,我们来查一查维基百科;
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-06_572c45a2a9087.jpg)
百度给出的答案太玄乎了,我们这里只讨论编程语言的对象,准确的说是JavaScript的对象;
**理解对象**
引用类型的值(对象)是引用类型的一个实例。在Js中,引用类型是一种数据结构,用于将数据和功能组织在一起。它有时被人称之为类,但这种称呼并不妥当。尽管JS从技术上讲是一门面向对象的语言,但它不具备传统的面向对象语言所支持的类和接口等基本结构。引用类型有时候也被称为对象定义,因为它们描述的是一类对象所具有的属性和方法。
在Java语言里面,我们可以把类理解称为是与现实世界中抽象出来的一种事物,比如人,而对象可以理解称为是市类的一个具体载体,与现实生活中存在或者不存在的具体事物相互对应,比如男人,女人,人妖,人马,妖人。在这里类与对象都是抽象的,只要对象比类的范围更细,更具体就可以了,比如对象时一个男人,我们说这个类就可以叫做张三,李四等;
如前所述,对象是某个特定引用类型的实例。新对象是使用new操作符后跟一个构造函数来创建的。构造函数本身就是一个函数。
**搞对象前,先创建一个对象:**
**1.对象字面量。**
~~~
var person={
name:'张三',
age:22,
address:'天堂',
showPresent:function(){
alert(this.name+" "+this.age+" "+this.address);
}
};
person.showPresent(); //调用
~~~
**2.函数create创建(有的浏览器不支持少用为好)**
Object.create(proto [, propertiesObject ]) 是E5中提出的一种新的对象创建方式,第一个参数是要继承的原型,如果不是一个子函数,可以传一个null,第二个参数是对象的属性描述符,这个参数是可选的
**3.构造函数模式**
~~~
function person(name,age,address){
this.name = name;
this.age = age;
this.address = address;
this.showPresent = function(){
alert(this.name+":"+this.age+":"+this.address);
}
}
var newPerson =new person('张三',22,'地狱');
alert(newPerson.showPresent());
~~~
构造函数的缺点:由于this指针在对象实例的时候发生改变指向新的实例。这时新实例的方法也要重新创建,如果n个实例就要n次重建相同的方法。于是让我们来揭开原型模式吧
**4.原型模式**
~~~
function person(){};
person.prototype.name='张三';
person.prototype.age=18;
person.prototype.address='湖北';
person.prototype.showPresent=function(){
alert(this.name+":"+this.age+":"+this.address);
}
var newperson=new person();
newperson.showPresent();
~~~
理解原型:
既然有了对象,我们就需要去了解一下对象是怎么产生的,我们得去拜访一下老丈人和丈母娘看看他们是怎么产生这个对象的;
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-06_572c45a2bff1c.jpg)
深度理解原型模型很重要,
首先,每个函数都有一个prototype(原型)属性,这个指针指向的就是person.prototype对象。而这个原型对象在默认的时候有一个属性constructor,指向person,这个属性可读可写。而当我们在实例化一个对象的时候,实例newperson除了具有构造函数定义的属性和方法外(注意,只是构造函数中的),还有一个指向构造函数的原型的指针,ECMAScript管他叫[[prototype]],这样实例化对象的时候,原型对象的方法并没有在某个具体的实例中,因为原型没有被实例。
所以这种模式定义的对象,在调用方法的时候过程:调用nnewperson.showPresent();.showPresent();先看实例中有没有,有调之,无追踪到原型,有调之,无出错,调用失败。
propertiesObject 参数的详细解释:(默认都为false)
**数据属性**
- writable:是否可任意写
- configurable:是否能够删除,是否能够被修改
- enumerable:是否能用 for in 枚举
- value:值
**访问属性:**
- get(): 访问
- set(): 设置
属性操作:一般情况下操作属性直接使用obj.name=value;这种方式直接操作就好了,但是在遍历循环的时候我们要使用
obj[name]=value;这种操作才能得到我们想要的结果,当然也可以使用get和set方法;删除的属性值的话我们可以使用delete obj.name;如果是检测属性的话我们可以直接 * in,也可以使用hasOwnproperty;他和in 的最大区别在于in 会去遍历该对象的所有属性,包括默认的,而hasOwnproperty只遍历该对象本身创建的;propertyIsEnumerable是判断该属性是否可以被枚举的;
~~~
<script type="text/javascript">
var cat=new Object();
cat.legs=4;
cat.name='奔驰';
'legs' in cat; //true
'aaa' in cat; //false
'toString' in cat; //true
cat.hasOwnProperty('legs'); //true
cat.hasOwnProperty('toString'); //fasle
cat.propertyIsEnumerable('legs'); //true
cat.propertyIsEnumerable('toString'); //fasle
</script>
~~~
JavaScript和JQuery和angularjs操作select
最后更新于:2022-04-01 11:43:55
今天使用angularjs操作select下拉列表,发现了一些问题,这里特意来总结一下他和JS,以及jquery的用法;
**<select>标签定义和用法**
select 元素可创建单选或多选菜单。当提交表单时,浏览器会提交选定的项目,或者收集用逗号分隔的多个选项,将其合成一个单独的参数列表,并且在将 <select> 表单数据提交给服务器时包括 name 属性。
**<select>标签可选的属性**
属性 值 描述
disabled disabled 规定禁用该下拉列表。
multiple multiple 规定可选择多个选项。
name name 规定下拉列表的名称。
size number 规定下拉列表中可见选项的数目。
**<select>标签全局属性**
<select> 标签支持 HTML 中的全局属性。
**<select>标签事件属性**
<select> 标签支持 HTML 中的事件属性。
**<select>标签用法举例**
<select name="list">
<option value="1">test1</option>
<option value="2">test2</option>
<option value="3">test3</option>
</select>
设置默认选中可在option 中添加 selected = "selected",具体举例如下:
<option value="2" selected="selected">test2</option>
给某个option 添加 selected = "selected" 属性就是默认选项,当然也可以简写
成selected;
**操作<select>标签**
1.动态创建select
~~~
function createSelect(){
var mySelect = document.createElement("select");
mySelect.id = "mySelect";
document.body.appendChild(mySelect);
}
~~~
这里要注意一点是这段代码必须放在body后面执行;而且这里动态添加也可以通过ID来添加,这样定位会更精确一点
在日常开发中也用的最多;
~~~
<body >
<div id="selectDiv">
</div>
</body>
<script>
function createSelect(){
var mySelect = document.createElement("select");
mySelect.id = "mySelect";
var div=document.getElementById("selectDiv");
div.appendChild(mySelect);
}
~~~
**2.添加选项option**
~~~
function addOption(){
//根据id查找对象,
var obj=document.getElementById('mySelect');
//添加一个选项
obj.add(new Option("文本","值"));
}
~~~
**3.动态添加选项option**
在开发中我们经常会用到动态的去添加option里面的值,我们也没去和后台交互,最简单的办法就是通过ajax请求,
得到后台传过来的数据,然后我们去拼装这个option格式的字符串,最后去显示;
~~~
$.ajax({
url:'action',
type:'post',
data:data,
async : false, //默认为true 异步
error:function(){
alert('error');
},
success:function(data){
$("#mySelect").append(data);
}
});
~~~
当然这种方法比较笨重,如果使用angularjs的话,就可以在后台得到这个下拉框的list集合,然后通过页面取遍历
~~~
<select ng-model="select.Myselect">
<option ng-repeat="item in list" value="item.id">
{{item.value}}
</option>
</select>
~~~
**4.删除所有选项option**
~~~
function removeAll(){
var obj=document.getElementById('mySelect');
obj.options.length=0;
}
~~~
**5.删除一个选项option**
~~~
function removeOne(){
var obj=document.getElementById('mySelect');
//index,要删除选项的序号,这里取当前选中选项的序号
var index=obj.selectedIndex;
obj.options.remove(index);
}
~~~
**6.获得选项option的值**
~~~
var obj = document.getElementByIdx_x(”testSelect”); //定位id
var index = obj.selectedIndex; // 选中索引
var text = obj.options[index].text; // 选中文本
var value = obj.options[index].value; // 选中值
jQuery中获得选中select值
function removeOne(){
var obj=document.getElementById('mySelect');
//index,要删除选项的序号,这里取当前选中选项的序号
var index=obj.selectedIndex;
obj.options.remove(index);
}
第一种方式
$('#testSelect option:selected').text();//选中的文本
$('#testSelect) .val();//选中的值
$("#testSelect ").get(0).selectedIndex;//索引
第二种方式
$("#tesetSelect").find("option:selected").text();//选中的文本
…….val();
…….get(0).selectedIndex;
~~~
**7.修改选项option**
var obj=document.getElementById('mySelect');
var index=obj.selectedIndex; //序号,取当前选中选项的序号
var val = obj.options[index]=new Option("新文本","新值");
**8.删除select**
function removeSelect(){
var mySelect = document.getElementById("mySelect");
mySelect.parentNode.removeChild(mySelect);
}
JavaScript垃圾收集
最后更新于:2022-04-01 11:43:53
JS具体自动垃圾收集机制(Java也有GC);也就是说,执行环境会负责管理代码执行过程中使用的内存。在编写JS程序时,开发人员不用再关心内存使用问题,所需内存的分配以及无用内存的回收完全实现了自动管理。这种垃圾收集器会按照固定的时间间隔(或代码中预定的收集时间),周期性的执行这一操作;
下面让我们一起来分析一下函数中局部变量的正常生命周期。局部变量只在函数执行的过程中存在,而在这个过程中,会为局部变量分配相应的内存空间,以便存储它们的值。然后在函数中使用这些变量,直至函数执行结束。此时局部变量就没有存在的必要了,因此可以释放内存以供将来使用。对于垃圾Js垃圾收集来说一般也就是两种策略,这两种策略感觉和Java很相似;
**标记清除:**
JS中最常用的垃圾收集方式是标记清楚(mark-and-swecp)。当变量进入环境时,我们会标记为“进入环境,并且给它一个标示。从逻辑上讲,永远不能释放进入环境的变量所占用的内存,因为只要进入到了相应的环境,那么我们的浏览器就有可能用到它们,而当变量离开环境时。则将其标记为离开环境;
可以在使用任何方式来标记变量。比如,可以通过翻转某个特殊的位来记录一个变量何时进入环境,或者使用一个进入环境的变量列表及一个离开环境的变量列表来跟踪哪个变量发生了变化。说到底,如何标记变量其实并不重要,关键在于采取什么策略。
垃圾收集器在运行的时候会给存储在内存中的所有变量都加上标记,然后,它会去掉环境中的变量及被环境中的变量引用的变量的标记。而在此之后再被加上离开环境时标记的变量讲被视为准备删除的变量,原因是环境中的变量已经无法访问到这些变量了,最后,垃圾收集器完成内存的清除工作。销毁哪些带标记的值,并回收他们所占用的内存空间。
**引用计数:**
另一种不太常见的垃圾收集策略叫做引用计数(Reference counting)。引用计数的含义是跟踪记录每个值被引用的次数。当一个变量引用了另一个变量的时候,则这个值就会加1,如果一个变量又赋值给了另外一个变量,则再加1.相反如果又没有引用这个值的变量了,那么会减1.当这个值的引用次数为0时,则说明没有办法在访问这个值了,因而可以将其占用的内存空间回收回来。这样,当垃圾收集器下次再执行时,它就会释放那些引用次数为零的值所占用的内存。
但是这样有一个很严重的Bug,就是循环引用。即A对象中包含一个指向对象B的指针,而对象B中也包含一个指向对象A的引用。
~~~
function problem(){
var objectA=new Object();
var objectB=new Object();
objectA.aobject=objectB;
objectB.bobject=objectA;
}
~~~
在上面的这个例子中,两个对象的属性相互引用;也就是说。这两个对象的引用次数都是2.在采用标记清除策略的实现中,当函数执行完毕后,两个变量还继续存在,因为它们的引用次数永远都不会是0.假如这个函数被重复多次调用,就会导致大量内存得不到回收。为此现在用的最多的就是标记清除。可是引用计数导致的麻烦还未就此终结;
**性能问题:**
垃圾收集器是周期性运行的,而且如果为变量分配的内存数量很客观,那么回收工作量也是相当大的,在这种情况下,确定垃圾收集的时间间隔是一个非常重要的问题。说到垃圾收集器多长时间运行一次,IE的垃圾收集器是根据内存分配量运行的,具体一点说就是256个变量,4096个对象或数组字面量和数组元素(slot)或者64KB的字符串,达到上述任何一个临界值,垃圾收集器就会运行。这种实现方式的问题在于,如果一个脚本中包含那么多变量,那么该脚本很可能会在生命周期
中一直保存着那么多变量,而这样一来,垃圾收集器就不得不频繁的运行。后来IE7就重写了其垃圾收集例程。
管理内存:
使用具备垃圾收集机制的语言编写程序,开发人员一般不必担心内存管理的问题,但是Js在进行内存管理和垃圾收集时面临的问题还是于众不同。最主要的就是分配给微博web浏览器的内存数量要比桌面应用程序少。这样是出于安全方面考虑,内存限制问题不仅会影响给变量分配内存,同时还是影响调用栈以及一个线程中能够同时执行的语句数量。
因为优化内存占用是一个好习惯,一旦数据不再用,最好设置为null,来释放其引用,这个做法叫做接触引用。
**GC优化策略:**
(1)分代回收(Generation GC)
这个和Java回收策略思想是一致的。目的是通过区分“临时”与“持久”对象;多回收“临时对象”区(young generation),少回收“持久对象”区(tenured generation),减少每次需遍历的对象,从而减少每次GC的耗时。
(2)增量GC
这个方案的思想很简单,就是“每次处理一点,下次再处理一点,如此类推”
JavaScript执行环境及作用域
最后更新于:2022-04-01 11:43:51
**执行环境(executin context)**是JS中最为重要的一个概念。执行环境定义了变量或函数有权访问的其他数据决定了它们各自的行为。每个执行环境都有一个与之关联的变量对象(variable object),环境中定义的所有变量和函数都保存在这个对象中。虽然我们编写的代码无法访问这个对象
,但解析器在处理数据时会在后台使用它。
全局执行环境是最外围的一个执行环境。根据JS实现的宿主环境不同,表示执行环境的对象也不一样。在Web浏览器中,全局执行环境被认为是Window对象,因此所有全局变量和函数都是作为window对象的属性和方法创建的。某个执行环境中的所有代码执行完毕后,改环境被销毁,保存在其中的所有变量和函数定义也随之销毁(全局执行环境直到应用程序退出-例如关闭网页或浏览器时才会被销毁);
每个函数都有自己的执行环境。当执行流进入一个函数时,函数的环境就会被推入一个环境栈中。而在函数执行之后,栈将其环境弹出,把控制权返回给之前的执行环境。JS程序中的执行流正是由这个方便的机制控制着。
当代码在一个环境中执行时,会创建变量对象的一个作用链域(scope chain)。作用域链的用途,是保证对执行环境有权访问的所有变量和函数的有序访问。作用域链的前端,始终都是当前执行的代码所在环境的变量对象。如果这个环境是函数,则将其活动对象(activation object)作为变量对象。活动对象在最开始时只包含一个变量,即arguments对象(这个对象在全局环境变量中是不存在的)。作用域链中的下一个变量对象来自包含(外部)环境,而再下一个变量对象则来自下一个包含环境。这样,一直延续到全局执行环境;全局执行环境的变量对象始终都是作用域中的最后一个对象。
标识符解析是沿着作用域一级一级地搜索标识符的过程。搜索过程始终从作用域链的前端开始,然后逐级的向后回溯,直至找到标识符为止(如果找不到标识符,通常会导致错误发生)。
~~~
var color="blue";
function changeColor(){
if(color==='blue'){
color="red"
}else
color="blue";
}
changeColor();
alert("Color is now "+color);
~~~
在这个简单的例子中,函数changeColor()的作用域链包含两个对象:它自己的变量对象(其中定义着arguments对象)和全局环境的变量对象。可以在函数内部访问变量color,就是因为可以再这个作用域链中找到它。
此外,在局部作用域中定义的变量可以再局部环境中与全局变量互换使用。
~~~
var color="blue";
function changeColor(){
var color1="red";
function swapColors(){
var color2=color1;
color1=color;
//这里可以访问所有的变量
}
//这里只能访问color1和color;
}
//这里只能访问color;
changeColor();
alert("Color is now "+color);
~~~
以上代码共涉及三个执行环境:全局环境,changeColor()的局部变量和swapColors()的局部变量环境。方法函数里面可以访问全局变量,但是无法访问比这个方法函数范围更小的局部变量,因为那两个环境都是它的父执行环境。
内部环境可以通过作用域链访问所有的外部环境,但外部环境不能访问内部环境中的任何变量和函数。这些环境之间的联系是线性,有次序的。每个环境都可以向上搜索作用域链,以查询变量和函数名;但任何环境不能通过向下搜索作用域链而进入另一个执行环境。在上面的这个例子中swapColors里包含三个对象:他会先从自己的变量对象中搜索变量和函数名,如果搜索不到则再搜索上一级作用域链。
**延长作用域链:**
虽然执行环境的类型只有两种-全局和局部。但还是有其他办法来延长作用域链。就是在有些语句可以再作用域链的前端临时增加一个变量对象,改变量对象会在代码执行后被移除。再两种情况下会发生这种现像。具体来说,就是当执行流进入下列任何语句的时候,作用域就会得到加长:
try-catch语句的catch块;
with语句;
这两个语句都会在作用域链的前端添加一个变量对象。对with语句来说,会将指定的对象添加到作用域链中。对catch语句来说,会创建一个新的变量对象,其中包含的是被抛出的错误对象的声明。
~~~
var add = function(a,b){
if(typeof a!="number"||typeof b!="number"){
throw{
name:"类型错误",
message:"参数需要number类型"
}
}
}
try{
add(1,"fg");
}catch(e){
alert(e.name);
}
finally{
alert("finally");
}
~~~
当我们执行到if这段代码的时候会创建异常类,我们在throw里面会指定异常的名称和异常的提示。再catch里面会创建一个新的变量对象,但是我们在这个对象里面,可以得到这个所抛出的异常,这就是创建一个新的变量对象,并且它的作用域延长了。
没有块级作用域:
JS没有块级作用域经常会导致理解上的困惑。在我比
较熟悉的Java语言里面,由花括号封闭的代码都有自己的作用域。因而支持根据条件来定义变量。
~~~
if(true){
var color="blue";
}
alert(color)
~~~
执行完这段代码,发现可以打印为blue;在Java中,color变量会在if语句执行完毕后被销毁。而在我们的JS中,if语句和for循环语句会被添加到当前的执行环境也就是全局变量中;
~~~
for(var i=0;i<=10;i++){
}
alert(i); //11
~~~
当执行完循环之后,i的值为11,有人说这里为什么不是10啦。我也很奇怪,在早起的JS里面这个i打印出来应该是10的,后来为了区分开来,在for循环结束之后又累加了一次。
**声明变量:**
使用var声明的变量会自动被添加到最近的环境中,也就是我们所谓的函数局部环境,在with语句中,最接近的环境是函数环境。如果初始化时变量没有使用var声明,该变量为自动被添加到全局环境。
~~~
function add(num1,num2){
sum=num1+num2;
return sum;
}
var result=add(1,2);
alert(sum); //3
~~~
**查询标示符:**
这个很好理解,当我们在某个环境中需要读取而引用一个标识符代表某种特定含义的时候,必须通过搜索来确定该标识符。搜索才有由近到远,由局部到全局,由小到大,如果查询到了相应的标示符将停止搜索;
~~~
var color="blue" ; //标示为黄色
function getColor(){
var color="red"; //标识为红色
return color;
}
alert(getColor()); //red
~~~
JavaScript基本类型和引用类型的值
最后更新于:2022-04-01 11:43:48
JS变量可能包含两种不同数据类型的值;基本类型值和引用类型值。基本类型指的是简单的数据段,而引用类型值那些由多个值构成的对象;
在将一个值赋给变量时,解析器必须确定这个值是基本类型值还是引用类型值。JS有5种基本数据类型:Undefined,
Null,Boolean,Number和String。这5种基本数据类型是按值访问的。因为可以操作保存在变量中的实际的值。
引用类型的值保存在内存中的对象。与其他语言不同,JavaScript不允许直接访问内存中的位置,也就是说不能直接操作对象的内存空间。在操作对象时,实际上是在操作对象的引用而不是实际的对象,为此,引用类型的值是按引用访问的。引用是js面向对象的基本概念之一。它是一个指向对象实际位置的指针。实际的对象肯定不会是引用。比如字符串永远是字符串,不过多个变量能够指向同一个对象。
对象可以中有多个原型属性(property)
动态的属性
定义基本类型值和引用类型值的方式是类似的:创建一个变量并为该变量赋值。但是,当这个值保存到变量中以后,对不同类型值可以执行的操作则很相同。对于引用类型的值,我们可以为其添加属性和方法每页可以移除改变和删除其属性和方法。
~~~
var person=new Object();
person.name="张三";
alert(person.name);
var name="李四";
name.age="13";
alert(name.age);
~~~
以上代码创建了一个对象将其保存在了变量person中。然后,我们为该对象添加了一个名字为name的属性,并为这个属性赋了值,当我打印这个属性的时候,是可以得到结果的。这说明如果对象不被销毁或者这个属性不被删除,则这个属性将一直存在。但是我们不能给基本数据类型添加属性。
复制变量值:
除了保持的方式不同之外,在从一个变量向另一个变量复制基本类型和引用类型的值时,也存在不同。如果从一个变量向另一个变量复制基本类型的值,会在变量对象上创建一个新值,然后把该值复制到位新变量分配的位置上;
~~~
var num1=5;
var num2=num1;
num1=3;
alert(num2);
~~~
在此num1种保存的值是5.当使用num1的值来初始化num2时,num2中也保存了值5。但num2中的5与num1中的5是完全独立的。该值只是num1中5的一个副本。此后,这两个变量可以参与任何操作互不影响。我觉得这一点和其他高级语言很相似例如java。
当从一个变量向另一个变量复制引用类型的值时,同样也会将存储在变量对象中的值复制一份到为新变量分配的空间中。不同的是们这个值的副本实际上是一个指针,而这个指针指向存储在堆中的一个对象,复制操作结束后,两个变量实际上将引用同一个对象。因此,改变其中一个变量,就会影响到另一个变量。这个跟引用类型的存储位置有关。
~~~
var obj1=new Object();
var obj2=obj1;
obj1.name="张三";
alert(obj2.name);
~~~
打印出来的是张三,这是因为obj1和obj2是两个引用,这两个引用指向的值都是Object();我们操作值的时候是通过引用的,当我们的对象发生改变的,其引用的值也发生了改变;
传递参数:
JS中所有函数的参数都是按值传递的。Java里面的参数有人认为是按值传递的,有人认为是按参数传递的,不过我写过一遍博客,里面专门讨论过这个,Java其实是按值传递的。也就是说,把函数外部的值复制给函数内部的参数,就和把值从一个变量复制到另一个变量一样。基本类型值的传递如同基本类型变量的复制一样,而引用变量传递也和复制一样。
在向参数传递基本类型的值时,被传递的值会被复制给一个局部变量(即命名参数,用JS的概念来说,就是arguments数组中的一个元素)。在向参数传递引用类型的值时,会把这个值在内存中的地址复制给一个局部变量,因此这个局部变量的变化会反映在函数的外部。
~~~
function add(num){
num+=10;
return num;
}
var count=20;
var resule=add(count);
alert(resule);
alert(count);
~~~
参数count和参数num互不影响,只是num复制了count的值而已;
~~~
function setName(obj){
obj.name="张三";
}
var person=new Object();
setName(person);alert(person.name);
~~~
以上代码创建了一个对象,并将其值保存在person引用中,然后调用setName函数,把值传递给obj,obj这个时候引用的对象和person引用的对象是同一个对象,所以里面修改了属性值,外面会有显示。有很多程序员错误的认为,在局部作用域中修改的对象会在全局作用域中反映出来,就说明是按引用传递的,这个说法不现实;
~~~
function setName(obj){
obj.name="张三";
obj=new Object();
obj.name="李四";
}
var person=new Object();
setName(person);alert(person.name);
~~~
如果是按引用传递的话,我们为这个引用重新创建了一个对象,并赋值,但是最后的结果还是张三,证明了是按值传递的;
JavaScript传参的问题
最后更新于:2022-04-01 11:43:46
**理解参数:**
JS函数的参数与大多数其他语言中函数的参数有所不同,JS函数不介意传递进来多少个参数,也不在乎传递进来的参数是什么数据类型。也就是说,即便你定义的函数只接收两个参数,在调用这个函数也未必一定要传递两个参数。可以传递一个,三个甚至不传递参数,而解析器永远也不会有什么怨言。原因是JavaScript中的参数在内部是用一个数组来表示的。函数接受到始终是这个数组,而不关心数组包含多少元素和参数。实际上在函数体内可以通过arguments对象来访问这个参数数组,从而获取传递给函数的每一个参数。
其实,arguments对象只是与数组类似(它并不是array的实例),因为可以使用方括号语法访问它的每一个元素(即第一个元素是arguments[0],第二个下标为1,以此类推),使用length属性来确定传递进来多少个参数。
~~~
function hello(){
alert(arguments[0]+":"+arguments[1]);
}
hello('姓名','张三');
~~~
JS如何实现重载:
在JS里面本身是没有重载这个概念的,但是JS可以在运行时重写我们的参数以及改变参数类型,这一点很类型与Java的重载;
~~~
public class Test {
public void outPut(String name){
System.out.println(name);
}
public void outPut(String name,String address){
System.out.println(name+":"+address);
}
public static void main(String[] args) {
Test test=new Test();
test.outPut("张三", "上海");
}
}
~~~
上面这段代码是Java的重载,它在运行的时候会根据方法的参数类型以及个数去匹配对应的参数;
~~~
function outPut(){
if(arguments.length==1){
alert(arguments[0]);
}else{
alert(arguments[0]+":"+arguments[1]);
}
};
outPut('张三','上海');
~~~
上面这段代码类似于JS的重载,即判断一下参数的个数,来得到去对应的参数信息;
另一个与参数有关的重要方面就是arguments对象可以与命名参数一起使用;
~~~
function outPut(name,address){
if(arguments.length==1){
alert(arguments[0]);
}else{
alert(arguments[0]+":"+address);
}
};
outPut('张三','上海');
~~~
关于arguments还有一个比较有意思的事,就是他的值会永远与命名参数的值保持同步;即我修改了name的值,那么他的arguments[0]的值也会发生改变,这一点跟Java很类似。不过这并不是说两个值访问的都是相同的内存空间;他们的的内存是独立的。但他们的值会同步,这种影响是双向的,即你修改命名参数的值会改变arguments中对应的值,修改arguments的值也会改变参数的值;
~~~
function outPut(name,address){
name="李四";
arguments[1]="武汉";
if(arguments.length==1){
alert(arguments[0]);
}else{
alert(arguments[0]+":"+address);
}
};
outPut('张三','上海');
~~~
关于参数还要记住最后一点,没有传递值的命名参数将自动被赋予undefined值。这就跟定义了遍历但还没有初始化一样。严格模式对如何使用arguments对象做出了一些限制。即像前面哪有赋值讲会无效;
JavaScript变量二(数据类型,Number,String,Object)
最后更新于:2022-04-01 11:43:44
**Number类型:**
Number类型是JavaScript中最令人关注也是最复杂的类型了。这种类型使用IEEE754格式来表示整数和浮点数值(浮点数值在某些语言中也被称为双精度数值).为支持各种数值类型,ECMA-262定义了不同的数值字面量格式。
主要分为十进制,八进制,十六进制。除了以十进制表示外,整数还可以通过八进制(以8为基数)或十六进制(以16为基数)的自面值来表示,其中,八进制字面值的第一位必须是(0),然后是八进制数字序列(0-7)。如果字面值中的数值超出了范围,那么前面的零将被忽略。请看下面的例子:
~~~
<script>
var intNum=10; //整数
var octalNum1=01010; //十进制的520
var octalNum2=018; //无效的八进制-解析为18;
</script>
~~~
浮点数值就是该数值必须包含一个小数点,并且小数点后面必须至少有一位数字。虽然小数点前面可以没有整数,但是不推荐这种写法;以下是浮点数值的几个例子:
~~~
<script>
var floatNum1=1.1;
var floatNum2=0.1;
var floatNum3=.1; //有效但不推荐
</script>
~~~
由于保存浮点数值需要的内存空间是保存整数值的两倍,因此ECMAScript会不失时机地将浮点数值转换为整数值。显然,如果小数点后面没有跟任何数字,那么这个数值就可以被作为整数值来保存。同样的,如果浮点数值本身表示的就是一个整数(如1.0),那么该值也会被转换为整数;
对于那些极大或极小的数值,可以用E表示法(即科学计算法)表示的浮点数值。用E表示法表示的数值等于E前面的数值乘以10的指数次幂。也可以使用E表示法表示极小的数值。如0.000000000000000001,这个数值可以用1E-7。浮点数值的最高精度是17位小数,但在进行算术计算时其精确度远远不如整数。例如,0.1加0.2的结果不是0.3,
而是其他值。这个小小的误差会导致我们无法测试特定的浮点数值。
例如
~~~
<script>
var f1=0.1;
var f2=0.2;
if(f1+f2==0.3){
alert("这是错误的");
}
</script>
~~~
如果我们非要去怎么判断的话,也可以使用如下方法;
~~~
var r = (0.3 - (0.1 + 0.2)).toFixed(2);
// r 是 string 的 -0.00
// 然后可以再把它转换成 float 类型
var f = parseFloat(r);
alert(f);
// 得到 f 是 -0,不过跟 0 是相等的 f === 0
~~~
**数值范围:**
由于内存的限制,ECMAScript并不能保存世界上所有的数值。ECMAScript能够表示的最小数值保存在Number.MIN_VALUE中-在大多数浏览器中,这个值是5e-324;能够表示的最大数值保存在Number.MAX_VALUE中-在大多数浏览器中,这个值是1.7976931348623157e+308。如果某次计算的结果得出了一个超出改范围的值,那么这个数值讲会被自动转换成特殊的Infinity值。具体来说就是正无穷和 负无穷;
**NaN:**
NaN,即非数值(Not a Number)是一个特殊的数值,这个数值用来表示本来要返回数值的操作数未返回数值的情况(这样就不会抛出错误了)。例如在Java里面,任何数值除以0都会导致错误,从而停止代码执行.但在javaScript中,任何数除以0都会返回Nan,因此不会影响其他代码的执行。
NaN本身有两个非同寻常的特点,首先,任何涉及NaN的操作都会返回NaN,其次任何值与Nan都不相等,包括NaN本身。
指对NaN的这两个特点,JS定义了isNaN()函数。这个函数接受一个参数,改参数可以是任何类型,而函数会尝试帮我们转换为数值,例如字符串类型的"10",或boolean值都可以;不能转换为数值会返回true;
**数值转换:**
有三个函数可以把非数值的转换为数值:Number(),parseInt,和parseFloat(),第一个函数可以用于任何数据类型,而另外两个函数只适用于字符串。
**String类型:**
String类型用于表示由零或多个16位Unicode字符组成的字符序列,即字符串。字符串可以由双引号(”)或单引号(')表示;
**1.字符字面量**
string数据类型包含一些特殊的字符字面量,也叫转义序列,用于表示非打印字符,或者具有其他用途的字符。
\n 换行; \t制表;\b空格;\r回车;\f进纸;\\斜杠;
**2.字符串的特点**
JS中的字符串是不可变得,也就是说,字符串一旦创建,它们的值就不能改变。要改变某个变量保存的字符串,首先要销毁原来的字符串,然后再用另一个包含新值的字符串填充改变量;
**Object类型:**
JS中的对象其实就是一组数据和功能的集合。对象可以通过执行new操作符后跟要创建的对象类型的名称来创建。而创建对象类型的实例并为其添加属性和方法,就可以创建自定义方法;
var o=new Object();
这一点跟java很像,但是如果我们不给构造函数传递参数,也可以省略后面的那一对圆括号;
这个 函数最常用的就是valuyeOf()和toString()方法,这两个返回对象的字符串,数值,或布尔值表示,通常与toString()方法的返回值相同。
JavaScript变量
最后更新于:2022-04-01 11:43:42
**变量**
JavaScript的变量是松散类型的,他不像JAva那样具有很严格的定义规范,所谓松散类型就是可以用来保存任何类型的数据,换句话说,每个变量仅仅是保存值的一个引用而已。定义变量时使用var操作符,后跟变量名,这个变量可以存储任何值,像这种未经定义的变量,会保存一个特殊的值-undefined,当我们定义了变量的值之后,初始化始也只是为这个变量赋了一个值而已;例如下面的:
~~~
<script>
var string="hello,world";
alert(string);
</script>
~~~
这段代码只是在初始化的时候为他赋了一个值而已,并不会根据类型而把他标注为字符串类型的;另外要注意的是,我们再声明变量的时候也可以不使用var 关键字,这样也不会出错,但是并不推荐,我们也可以在修改变量的同时修改值的类型,但是也不推荐;
~~~
<script>
string="hello,world";
string="123"
alert(string);
</script>
~~~
**数据类型**
在ECMAScript中有五种基本简单数据类型,(我们的java有八大基本数据类型。。)分别是:undefined,Null,Boolean,number,String。还有一种复杂数据类型-Object,object本质上是一组无序的名值对组成的。ECMAScript不支持任何创建自定义类型的机制,而所有值最终都将是上述6种数据类型之一。乍一看,好像只有6中数据类型不足以表示所有数据;但是,由于ECMAScript数据具有动态性,因此的确没有再定义其他数据类型的必要了。
我们可以使用typeof操作符来检验返回值的类型。
~~~
<script>
var test1;
alert(typeof test1); //undefined 如果这个值未定义;
var test2=true;
alert(typeof test2); //boolean布尔值;
var test3="abc";
alert(typeof test3); //string 如果这个值是字符串;
var test4=123;
alert(typeof test4); //number 如果这个值是数值;
var test5=new Object();
alert(typeof test5); //object如果这个值是对象;
function test6(){};
alert(typeof test6); //function 函数类型,声明的时候不要带var
</script>
~~~
**Undefined类型:**
Undefined类型只有一个值,即特殊的undefined,在使用var声明变量但未对其加以初始化时,这个变量的值就是undefined;这个跟java有所不同;
**Null类型:**
Null类型是第二个只有一个值的数据类型,这个特殊的值是null,从逻辑角度来看,null值表示一个空对象指针,而这也正是使用typeof操作符检测null值会返回“object”的原因,如果定义的变量准备在将来用于保存对象,那么最好将该变量初始化为null,而表示其他值。这样一来,只要直接检查null值就可以知道对应的变量是否保存了一个对象的引用;
实际上undefined值是派生自null值的;即undefined==null --true;
**Boolean类型:**
Boolean类型是ECMAScript中使用的最多的一种类型,该类型只要两个字面值:true和false,虽然Boolean类型的自面值只要两个,但是JavaScript的所有类型的值都可以转换为Boolean值;使用Boolean();
~~~
var test1="Hello world!";
if(Boolean(test1)){
alert(test1);
};
~~~
这一章,我们了解了JS的基本数据类型,下一次再深入去理解一下数值型和Object这两个特殊的类型吧;
javascript基础与定义
最后更新于:2022-04-01 11:43:39
JavaScript一种[直译](http://baike.baidu.com/view/295412.htm)式[脚本语言](http://baike.baidu.com/view/76320.htm),是一种动态类型、弱类型、基于原型的语言,内置支持类型。它的[解释器](http://baike.baidu.com/view/592974.htm)被称为JavaScript引擎,为[浏览器](http://baike.baidu.com/view/7718.htm)的一部分,广泛用于[客户端](http://baike.baidu.com/view/930.htm)的脚本语言,最早是在[HTML](http://baike.baidu.com/view/692.htm)([标准通用标记语言](http://baike.baidu.com/view/5286041.htm)下的一个应用)网页上使用,用来给[HTML](http://baike.baidu.com/view/692.htm)网页增加动态功能。
为什么JavaScript非常值得我们学习?
1. 所有主流浏览器都支持JavaScript。
2. 目前,全世界大部分网页都使用JavaScript。
3. 它可以让网页呈现各种动态效果。
4. 做为一个Web开发师,如果你想提供漂亮的网页、令用户满意的上网体验,JavaScript是必不可少的工具。
5. JavaScript的学习成本很低,只要有文本编辑器就可以允许;
下面开始使用JavaScript来写一个最简单的例子;
这里我们使用一款叫做Editplus的工具,桌面新建一个叫做hello.html的文档,然后右键点击EditPlus打开;
点击左上角新建HTML,复制里面的HTML格式内容到我们的hello.html里面;
接着引入javaScript标签,执行里面的代码;
~~~
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title> New Document </title>
<meta name="Generator" content="EditPlus">
<meta name="Author" content="">
<meta name="Keywords" content="">
<meta name="Description" content="">
<script>
alert("hello world");
</script>
</head>
<body>
</body>
</html>
~~~
前言
最后更新于:2022-04-01 11:43:37
> 原文出处:[JS重塑学习](http://blog.csdn.net/column/details/liao-js.html)
作者:[liaodehong](http://blog.csdn.net/liaodehong)
**本系列文章经作者授权在看云整理发布,未经作者允许,请勿转载!**
# JS重塑学习
> 有用过两年多的JS了,以前一直觉得不是很重要,但是做JavaWeb的,要做好的话,那么JS这门基础课程肯定要打扎实,现在准备利用空闲时间重新学习JS。