HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

js的try catch

js

jquery 让 dom 操作不报错,结果现在很多前端以为 try catch 都用不着了。
https://github.com/inexorabletash/polyfill/blob/master/es5.js
这里有个例子,通过try catch 监测浏览器是否实现某个功能是个好习惯,如果不支持,有时候可以采用降级处理。
那些不处理的异常的,估计也就写写网页吧,一个严肃的应用出现js报错是非常影响用户体验的事情!

如果你无法降级处理,做法也不应该是直接抛出异常,好的体验应该是告诉用户你的应用部分功能不支持他现在的浏览器,提示他更换,抛错会让用户无所适从。
下面用符合 ECMAscript 规范的简单的 JavaScript 来编写相同的“条件catch子句”(显然更加冗长的,但是可以在任何地方运行):
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/try...catch
try {
myroutine(); // may throw three types of exceptions
} catch (e) {
if (e instanceof TypeError) {
// statements to handle TypeError exceptions
} else if (e instanceof RangeError) {
// statements to handle RangeError exceptions
} else if (e instanceof EvalError) {
// statements to handle EvalError exceptions
} else {
// statements to handle any unspecified exceptions
logMyErrors(e); // pass exception object to error handler
}
}

try{
//正常执行
}catch(e/你感觉会出错的 错误类型/){
//可能出现的意外
e.g:用户自己操作失误 或者 函数少条件不影响下面的函数执行
//有时也会用在 比如focus()但ie有可能会第一次没有focus事件 再让他执行一次
//有时一些不是bug的bug 在ie上要求必须加上catch,哪怕就一个空catch
}

try catch的使用,永远应该放在你的控制范围之内,而不应该防范未知的错误。也就是说你很清楚知道这里 是有可能“出错的”,而且你很清楚知道什么前提下会出错,你就是要故意利用报错信息来区分错误,后续的程序会解决所有的出错,让程序继续执行。
不能让用户发现你根本没预料到的错误,而不是你先发现错误。
大多数情况下,try catch适用于两种场合:
1、浏览器原罪的场合:也就是兼容性场合,因为浏览器兼容性不是程序员能改正的,所以只能try catch:由于不同浏览器的报错提示是不一样的,根据捕获的浏览器的报错提示判断用户的浏览器,然后做出对应的措施,这时候使用try catch是巧妙的办法,如果用if就比较笨拙,因为if通常只能反馈真或假,不能直接反馈浏览器的报错内容。
2、考虑如下代码。window.a.b是非法的,再跟2对比就没有意义,这样非法的条件,在try catch中仍可以继续运行下去。但在if中window.a.b已经报错,整个页面都会坏掉。如果希望用if写,那么必须先判断window.a是否是合法的,window.a是合法的前提下再判断window.a.b是不是合法的,如果也是合法的,再判断window.a.b是否不等于2,这样是不是很蠢?这时就体现出try catch的妙处了,程序不但知道window.a.b !== 2是假的,而且直接可以知道究竟哪一步就已经是假的。
再想象一下,有一个变量是json.a.b.c,其中的a/b/c都可能是存在的也可能是不存在的,全看具体情况,这时候你简单的写if (json.a.b.c === 2) {...}是不行的,因为json.a.b就可能已经是非法的,所以你如果用if,就要考虑a是不是非法的、a是合法前提下b是不是非法的,b是合法前提下c是不是非法的。但是json.a.b.c === 2在try中就可以直接写,也就是说,我不关心究竟a/b/c谁是非法的,我只关心json.a.b.c到底是不是等于2,不等于2或者任何一步出错,对我来讲没有区别,反正都是不等于2,我不关心哪步出错,而且程序不会坏掉。这是一种比较省心的写法。
另外注意,try catch不能做真假判断,只能做非法判断。也就是说:try {1 === 2},虽然1===2是假,但是是合法的,catch不会捕捉到错误,也不会告诉你1 === 2到底是真是假。所以,写在try里的应该是json.a.b.c而不是json.a.b.c === 2。究竟是不是等于2,是后面的事,是if干的事。简单说,try catch用于捕捉报错,当你不关心哪一步错误,只关心有没有错,就用try catch。
try{
window.a.b !== 2;
}catch(err){
alert(err) //可执行
alert(123)//可执行
}

if(window.a.b !== 2){
alert("error") //不执行
}
alert(123) //不执行
try catch在早期被各种语言的程序员滥用,try catch出现的场合就被夸大了,事实上没那么多适用场合。如果你的几千行程序都没用到try catch也是很正常的,尤其是用了jquery。

  1. 1.事情还有得挽回,换条路走
    try {
    执行某个逻辑
    } catch(e){
    出问题了,换个逻辑执行
    }
    2.体面的退出
    try{
    正常流程
    }catch(e){
    弹个框告诉用户不好意思处理点问题
    如果是用户的错误就告诉用户什么地方错了
    如果是程序的错,就告诉用户不好意思没法执行
    }

  2. 异常处理和错误处理是两个不同的概念。例如NodeJS里大多数error都是用来处理异常的,因为异常是不可避免的,例如:数据库挂了,网络错误,你虽然知道有可能,但是不知道何时发生,这些异常你需要捕捉或者传给上层。而错误处理,这是一些基本的判定,可以从代码级别避免其发生,可预知可推测,如果发生了,不是系统问题,而是你的程序有bug了。
    对于NodeJS来说,两种错误都时刻需要注意,特别是系统错误,因为不可预知,需要大量代码来catch错误,传递错误,最后统一处理。
    而对于前端,系统错误出现的场景相对来说低得多,主要是一些i/o场景,大部分前端可能不太关心。而普通的错误处理,则比较常见,因为前端耦合的特定系统比较多,和这些系统操作的时候,数据和dom大多是可预知的,跟系统错误还是要区分开的,一些错误,需要你自己去吞并和处理,如果出现错误,显然是bug,而不是不可预知。

一句话解释:
try catch机制非常好。那些觉得try catch不行的人,是他们自己的水平有问题,无法理解这种机制。并且这群人写代码不遵守规则,喜欢偷懒,这才造成try catch不好的错觉。
详细解释:
1.程序要健壮,必须要设计报错机制。
最古老,也是最常见的,比如:
bool CreateFile( );
//如果创建文件失败就返回false,否则返回true。
这种报错方式,显然不好。因为它没有给出产生错误的具体原因。
2.改进:一个函数或过程,会因为不同的原因产生错误,报错机制必须要把这些错误原因进行区分后,再汇报。
比如:
int CreateFile():
//如果创建成功就返回1.
//如果是因为没有权限,导致失败,返回-1。
//如果是因为文件已经存在,导致失败,返回-2。
//如果是因为创建文件发生超时,导致失败,返回-3。
这样看上去,比【1】要好些,至少指出了比较具体的失败原因,但是,还不够。
3.很多情况下,函数需要把详细的原因,用字符串的方式,返回:
class Result{
....int State;//同【2】
....string ErrorMessage;//如果失败,这里将给出详细的信息,如果有可能,应该把建议也写上去。
}
Result CreateFile();
//如果创建成功,返回的Result,State为1,ErrorMessage为null。
//如果是因为没有权限,导致失败,返回的Result,State为-1,ErrorMessage为"用户【guest】没有权限在【C:\】这个目录下创建该文件。建议您向管理员申请权限,或者更换具有权限的用户。"。//如果是因为文件已经存在,导致失败,返回的Result,State为-2,ErrorMessage为"文件【C:\abc.txt】已经存在。如果需要覆盖,请添加参数:arg_overwrite = true"。//如果是因为创建文件发生超时,导致失败,返回的Result,State为-3,ErrorMessage为"在创建文件时超时,请使用chkdsk检查文件系统是否存在问题。"。

4.我个人推崇上面这种方式,完整,美观。但是这种流程,容易与正常的代码混在一起,不好区分开。因此,Java、C#等设计了try catch这一种特殊的方式:void CreateFile()//如果创建成功就不会抛出异常。//如果是因为没有权限,导致失败,会抛出AccessException,这个Exception的Msg属性为"用户【guest】没有权限在【C:\】这个目录下创建该文件。建议您向管理员申请权限,或者更换具有权限的用户。"。//如果是因为文件已经存在,导致失败,会抛出FileExistedException,这个Exception的Msg属性为"文件【C:\abc.txt】已经存在。如果需要覆盖,请添加参数:arg_overwrite = true"。//如果是因为创建文件发生超时,导致失败,会抛出TimeoutException,这个Exception的Msg属性为"在创建文件时超时,请使用chkdsk检查文件系统是否存在问题。"。可见,上述机制,实际上是用不同的Exception代替了【3】的State。
这种机制,在外层使用时:
try{
....CreateFile( "C:\abc.txt" );
}catch( AccessException e ){
....//代码进入这里说明发生【没有权限错误】
}
catch( FileExistedException e )
{
....//代码进入这里说明发生【文件已经存在错误】
}catch( TimeoutException e ){
....//代码进入这里说明发生【超时错误】
}
对比一下【3】,其实这与【3】本质相同,只是写法不同而已。
5.综上,我个人喜欢【3】这类面向过程的写法。但很多喜欢面向对象的朋友,估计更喜欢【4】的写法。然而【3】与【4】都一样。这两种机制都是优秀的错误处理机制。

  1. 理论说完了,回到正题,题注问:为什么不用try catch?
    这是因为,很多新手,他们是这样写代码的:
    void CreateFile( )
    //无论遇到什么错误,就抛一个 Exception,并且也不给出Msg信息。
    这样的话,在外层只能使用:
    try
    {
    ....CreateFile( "C:\abc.txt" );
    }
    catch( Exception e )
    {
    ....//代码进入这里说明发生错误
    }
    当出错后,只知道它出错了,并不知道是什么原因导致错误。这同【1】。

以及,即使CreateFile是按【4】的规则设计的,但新手在外层是这样使用的:
try
{
....CreateFile( "C:\abc.txt" );
}
catch( Exception e )
{
....//代码进入这里说明发生错误
....throw Exception( "发生错误" )
}
这种情况下,如果这位新手的同事,调用了这段代码,或者用户看到这个错误信息,也只能知道发生了错误,但并不清楚错误的原因。这与【1】是相同的。出于这些原因,新手的同事,以及用户,并没有想到,造成这个问题是原因新手的水平太差,写代码图简单省事。他们却以为是try catch机制不行。因此,这就导致了不建议用try catch。
<script language="JavaScript">
try
{
throw new Error(10,"asdasdasd")
}
catch (e)
{
alert(e.message);
alert(e.description)
alert(e.number)
alert(e.name)
throw new Error(10,"asdasdasd")
}

</script>
在JavaScript可以使用try...catch来进行异常处理。例如:
try {
foo.bar();
} catch (e) {
alert(e.name + ": " + e.message);
}
目前我们可能得到的系统异常主要包含以下6种:
EvalError: raised when an error occurs executing code in eval()
RangeError: raised when a numeric variable or parameter is outside of its valid range
ReferenceError: raised when de-referencing an invalid reference
SyntaxError: raised when a syntax error occurs while parsing code in eval()
TypeError: raised when a variable or parameter is not a valid type
URIError: raised when encodeURI() or decodeURI() are passed invalid parameters
上面的六种异常对象都继承自Error对象。他们都支持以下两种构造方法:
new Error();
new Error("异常信息");
手工抛出异常的方法如下:
try {
throw new Error("Whoops!");
} catch (e) {
alert(e.name + ": " + e.message);
}
如要判断异常信息的类型,可在catch中进行判断:
try {
foo.bar();
} catch (e) {
if (e instanceof EvalError) {
alert(e.name + ":" + e.message);
}
else if (e instanceof RangeError) {
alert(e.name + ": " + e.message);
}
// etc
}
Error具有下面一些主要属性:

description: 错误描述 (仅IE可用).
fileName: 出错的文件名 (仅Mozilla可用).
lineNumber: 出错的行数 (仅Mozilla可用).
message: 错误信息 (在IE下同description)
name: 错误类型.
number: 错误代码 (仅IE可用).
stack: 像Java中的Stack Trace一样的错误堆栈信息 (仅Mozilla可用).
因此为了更好的了解错误信息我们可以将catch部分改为如下形式:
try {
foo.bar();
} catch (e) {
if (browserType != BROWSER_IE) {
alert("name: " + e.name +
"message: " + e.message +
"lineNumber: " + e.lineNumber +
"fileName: " + e.fileName +
"stack: " + e.stack);
}
else {
alert("name: " + e.name +
"errorNumber: " + (e.number & 0xFFFF ) +
"message: " + e.message");
}
}
JavaScript中的throw命令事实上可以抛出任何对象,并且我们可以在catch接受到此对象。例如:
try {
throw new Date(); // 抛出当前时间对象
} catch (e) {
alert(e.toLocaleString()); // 使用本地格式显示当前时间
}

之前一直没有去研究try catch对代码运行的性能影响,只是一直停留在了感觉上,正好最近开会交流学习的时候,有人提出了相关的问题。借着周末,正好研究一番。
window.JSTracker=window.JSTracker||[];
try{
//your code
}catch(e){
JSTracker.push(e);
throwe;//建议将错误再次抛出,避免测试无法发现异常
}
设计实验方式
简单的设计方案也就是对比实验。
空白组1:[无 try catch 的情况下对数据取模1千万次耗时]
<!DOCTYPEhtml>
<html>
<head>
<title>1无trycatch的情况耗时</title>
<script>
!function(){
//无try catch的情况耗时
var t = newDate();
//耗时代码开始
for(vari=0;i<100000000;i++){
varp=i%2;
}
//耗时代码结束
document.write(newDate()–t);
}();
</script>
</head>
<body>
</body>
</html>
参照组2:[将耗时代码用 try 包围,内联耗时代码]
<!DOCTYPEhtml>
<html>
<head>
<title>2在try中内联代码的耗时情况</title>
<script>
!function(){
//在 try 中内联代码的耗时情况
vart=newDate();
try{
//耗时代码开始
for(vari=0;i<100000000;i++){
varp=i%2;
}
//耗时代码结束
thrownewError();
}catch(e){
}
document.write(newDate()–t);
}();
</script>
</head>
<body>
</body>
</html>
参照组3:[将耗时代码用 try 包围,外联耗时代码]
?
<!DOCTYPEhtml>
<html>
<head>
<title>3在try中内联代码的耗时情况</title>
<script>
!function(){
functionrun(){
//耗时代码开始
for(vari=0;i<100000000;i++){
varp=i%2;
}
//耗时代码结束
}
//在 try 中内联代码的耗时情况
vart=newDate();
try{
run();
thrownewError();
}catch(e){
}
document.write(newDate()–t);
}();
</script>
</head>
<body>
</body>
</html>
参照组4:[将耗时代码用 catch 包围,内联耗时代码]
?
<!DOCTYPEhtml>
<html>
<head>
<title>4在catch中内联代码的耗时情况</title>
<script>
!function(){
//在 catch 中内联代码的耗时情况
vart=newDate();
try{
thrownewError();
}catch(e){
//耗时代码开始
for(vari=0;i<100000000;i++){
varp=i%2;
}
//耗时代码结束
}
document.write(newDate()–t);
}();
</script>
</head>
<body>
</body>
</html>
参照组5:[将耗时代码用 catch 包围,外联耗时代码]
?
<!DOCTYPEhtml>
<html>
<head>
<title>5在catch中内联代码的耗时情况</title>
<script>
!function(){
functionrun(){
//耗时代码开始
for(vari=0;i<100000000;i++){
varp=i%2;
}
//耗时代码结束
}
//在 catch 中内联代码的耗时情况
vart=newDate();
try{
thrownewError();
}catch(e){
run();
}
document.write(newDate()–t);
}();
</script>
</head>
<body>
</body>
</html>
运行结果(只选取了 Chrome 作为示例)
– 不使用 TRY-CATCH TRY 中耗时,内联代码 TRY 中耗时,外联代码 CATCH 中耗时,内联代码 CATCH 中耗时,外联代码
Chrome51 98.2 1026.9 107.7 1028.5 105.9
给出总结
使用 try catch 的使用无论是在 try 中的代码还是在 catch 中的代码性能消耗都是一样的。
需要注意的性能消耗在于 try catch 中不要直接塞进去太多的代码(声明太多的变量),最好是吧所有要执行的代码放在另一个 function 中,通过调用这个 function 来执行。
针对第二点,可以查看 ECMA 中关于 try catch 的解释,在代码进入 try catch 的时候 js引擎会拷贝当前的词法环境,拷贝的其实就是当前 scope 下的所有的变量。
建议
在使用 try catch 的时候尽量把 try catch 放在一个相对干净的 scope 中,同时在 try catch 语句中也尽量保证足够少的变量,最好通过函数调用方式来 try catch。
试验中的现象解释
测试过程中还是发现了一个疑问, 以下两段代码在 Chrome 44 中运行出来的结果差距非常大,加了一句空的 try catch 之后平均为:850ms,加上之前为:140ms。
?

!function(){
//无 try catch 的情况耗时
vart=newDate();
//耗时代码开始
for(vari=0;i<100000000;i++){
varp=i%2;
}
//耗时代码结束
document.write(newDate()–t);
try{
}catch(e){
}
}();
!function(){
//无 try catch 的情况耗时
vart=newDate();
//耗时代码开始
for(vari=0;i<100000000;i++){
varp=i%2;
}
//耗时代码结束
document.write(newDate()–t);
}();
其实原因很简单
只要把代码改为这样 耗时就降下来了:
?

!function(){
!function(){
//无 try catch 的情况耗时
vart=newDate();
//耗时代码开始
for(vari=0;i<100000000;i++){
varp=i%2;
}
//耗时代码结束
document.write(newDate()–t);
}();
try{
}catch(e){
}
}();
总结
以上就是关于使用try catch对代码运行性能影响的全部内容。
来源:http://www.jb51.net/article/101291.htm

继续阅读 »

jquery 让 dom 操作不报错,结果现在很多前端以为 try catch 都用不着了。
https://github.com/inexorabletash/polyfill/blob/master/es5.js
这里有个例子,通过try catch 监测浏览器是否实现某个功能是个好习惯,如果不支持,有时候可以采用降级处理。
那些不处理的异常的,估计也就写写网页吧,一个严肃的应用出现js报错是非常影响用户体验的事情!

如果你无法降级处理,做法也不应该是直接抛出异常,好的体验应该是告诉用户你的应用部分功能不支持他现在的浏览器,提示他更换,抛错会让用户无所适从。
下面用符合 ECMAscript 规范的简单的 JavaScript 来编写相同的“条件catch子句”(显然更加冗长的,但是可以在任何地方运行):
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/try...catch
try {
myroutine(); // may throw three types of exceptions
} catch (e) {
if (e instanceof TypeError) {
// statements to handle TypeError exceptions
} else if (e instanceof RangeError) {
// statements to handle RangeError exceptions
} else if (e instanceof EvalError) {
// statements to handle EvalError exceptions
} else {
// statements to handle any unspecified exceptions
logMyErrors(e); // pass exception object to error handler
}
}

try{
//正常执行
}catch(e/你感觉会出错的 错误类型/){
//可能出现的意外
e.g:用户自己操作失误 或者 函数少条件不影响下面的函数执行
//有时也会用在 比如focus()但ie有可能会第一次没有focus事件 再让他执行一次
//有时一些不是bug的bug 在ie上要求必须加上catch,哪怕就一个空catch
}

try catch的使用,永远应该放在你的控制范围之内,而不应该防范未知的错误。也就是说你很清楚知道这里 是有可能“出错的”,而且你很清楚知道什么前提下会出错,你就是要故意利用报错信息来区分错误,后续的程序会解决所有的出错,让程序继续执行。
不能让用户发现你根本没预料到的错误,而不是你先发现错误。
大多数情况下,try catch适用于两种场合:
1、浏览器原罪的场合:也就是兼容性场合,因为浏览器兼容性不是程序员能改正的,所以只能try catch:由于不同浏览器的报错提示是不一样的,根据捕获的浏览器的报错提示判断用户的浏览器,然后做出对应的措施,这时候使用try catch是巧妙的办法,如果用if就比较笨拙,因为if通常只能反馈真或假,不能直接反馈浏览器的报错内容。
2、考虑如下代码。window.a.b是非法的,再跟2对比就没有意义,这样非法的条件,在try catch中仍可以继续运行下去。但在if中window.a.b已经报错,整个页面都会坏掉。如果希望用if写,那么必须先判断window.a是否是合法的,window.a是合法的前提下再判断window.a.b是不是合法的,如果也是合法的,再判断window.a.b是否不等于2,这样是不是很蠢?这时就体现出try catch的妙处了,程序不但知道window.a.b !== 2是假的,而且直接可以知道究竟哪一步就已经是假的。
再想象一下,有一个变量是json.a.b.c,其中的a/b/c都可能是存在的也可能是不存在的,全看具体情况,这时候你简单的写if (json.a.b.c === 2) {...}是不行的,因为json.a.b就可能已经是非法的,所以你如果用if,就要考虑a是不是非法的、a是合法前提下b是不是非法的,b是合法前提下c是不是非法的。但是json.a.b.c === 2在try中就可以直接写,也就是说,我不关心究竟a/b/c谁是非法的,我只关心json.a.b.c到底是不是等于2,不等于2或者任何一步出错,对我来讲没有区别,反正都是不等于2,我不关心哪步出错,而且程序不会坏掉。这是一种比较省心的写法。
另外注意,try catch不能做真假判断,只能做非法判断。也就是说:try {1 === 2},虽然1===2是假,但是是合法的,catch不会捕捉到错误,也不会告诉你1 === 2到底是真是假。所以,写在try里的应该是json.a.b.c而不是json.a.b.c === 2。究竟是不是等于2,是后面的事,是if干的事。简单说,try catch用于捕捉报错,当你不关心哪一步错误,只关心有没有错,就用try catch。
try{
window.a.b !== 2;
}catch(err){
alert(err) //可执行
alert(123)//可执行
}

if(window.a.b !== 2){
alert("error") //不执行
}
alert(123) //不执行
try catch在早期被各种语言的程序员滥用,try catch出现的场合就被夸大了,事实上没那么多适用场合。如果你的几千行程序都没用到try catch也是很正常的,尤其是用了jquery。

  1. 1.事情还有得挽回,换条路走
    try {
    执行某个逻辑
    } catch(e){
    出问题了,换个逻辑执行
    }
    2.体面的退出
    try{
    正常流程
    }catch(e){
    弹个框告诉用户不好意思处理点问题
    如果是用户的错误就告诉用户什么地方错了
    如果是程序的错,就告诉用户不好意思没法执行
    }

  2. 异常处理和错误处理是两个不同的概念。例如NodeJS里大多数error都是用来处理异常的,因为异常是不可避免的,例如:数据库挂了,网络错误,你虽然知道有可能,但是不知道何时发生,这些异常你需要捕捉或者传给上层。而错误处理,这是一些基本的判定,可以从代码级别避免其发生,可预知可推测,如果发生了,不是系统问题,而是你的程序有bug了。
    对于NodeJS来说,两种错误都时刻需要注意,特别是系统错误,因为不可预知,需要大量代码来catch错误,传递错误,最后统一处理。
    而对于前端,系统错误出现的场景相对来说低得多,主要是一些i/o场景,大部分前端可能不太关心。而普通的错误处理,则比较常见,因为前端耦合的特定系统比较多,和这些系统操作的时候,数据和dom大多是可预知的,跟系统错误还是要区分开的,一些错误,需要你自己去吞并和处理,如果出现错误,显然是bug,而不是不可预知。

一句话解释:
try catch机制非常好。那些觉得try catch不行的人,是他们自己的水平有问题,无法理解这种机制。并且这群人写代码不遵守规则,喜欢偷懒,这才造成try catch不好的错觉。
详细解释:
1.程序要健壮,必须要设计报错机制。
最古老,也是最常见的,比如:
bool CreateFile( );
//如果创建文件失败就返回false,否则返回true。
这种报错方式,显然不好。因为它没有给出产生错误的具体原因。
2.改进:一个函数或过程,会因为不同的原因产生错误,报错机制必须要把这些错误原因进行区分后,再汇报。
比如:
int CreateFile():
//如果创建成功就返回1.
//如果是因为没有权限,导致失败,返回-1。
//如果是因为文件已经存在,导致失败,返回-2。
//如果是因为创建文件发生超时,导致失败,返回-3。
这样看上去,比【1】要好些,至少指出了比较具体的失败原因,但是,还不够。
3.很多情况下,函数需要把详细的原因,用字符串的方式,返回:
class Result{
....int State;//同【2】
....string ErrorMessage;//如果失败,这里将给出详细的信息,如果有可能,应该把建议也写上去。
}
Result CreateFile();
//如果创建成功,返回的Result,State为1,ErrorMessage为null。
//如果是因为没有权限,导致失败,返回的Result,State为-1,ErrorMessage为"用户【guest】没有权限在【C:\】这个目录下创建该文件。建议您向管理员申请权限,或者更换具有权限的用户。"。//如果是因为文件已经存在,导致失败,返回的Result,State为-2,ErrorMessage为"文件【C:\abc.txt】已经存在。如果需要覆盖,请添加参数:arg_overwrite = true"。//如果是因为创建文件发生超时,导致失败,返回的Result,State为-3,ErrorMessage为"在创建文件时超时,请使用chkdsk检查文件系统是否存在问题。"。

4.我个人推崇上面这种方式,完整,美观。但是这种流程,容易与正常的代码混在一起,不好区分开。因此,Java、C#等设计了try catch这一种特殊的方式:void CreateFile()//如果创建成功就不会抛出异常。//如果是因为没有权限,导致失败,会抛出AccessException,这个Exception的Msg属性为"用户【guest】没有权限在【C:\】这个目录下创建该文件。建议您向管理员申请权限,或者更换具有权限的用户。"。//如果是因为文件已经存在,导致失败,会抛出FileExistedException,这个Exception的Msg属性为"文件【C:\abc.txt】已经存在。如果需要覆盖,请添加参数:arg_overwrite = true"。//如果是因为创建文件发生超时,导致失败,会抛出TimeoutException,这个Exception的Msg属性为"在创建文件时超时,请使用chkdsk检查文件系统是否存在问题。"。可见,上述机制,实际上是用不同的Exception代替了【3】的State。
这种机制,在外层使用时:
try{
....CreateFile( "C:\abc.txt" );
}catch( AccessException e ){
....//代码进入这里说明发生【没有权限错误】
}
catch( FileExistedException e )
{
....//代码进入这里说明发生【文件已经存在错误】
}catch( TimeoutException e ){
....//代码进入这里说明发生【超时错误】
}
对比一下【3】,其实这与【3】本质相同,只是写法不同而已。
5.综上,我个人喜欢【3】这类面向过程的写法。但很多喜欢面向对象的朋友,估计更喜欢【4】的写法。然而【3】与【4】都一样。这两种机制都是优秀的错误处理机制。

  1. 理论说完了,回到正题,题注问:为什么不用try catch?
    这是因为,很多新手,他们是这样写代码的:
    void CreateFile( )
    //无论遇到什么错误,就抛一个 Exception,并且也不给出Msg信息。
    这样的话,在外层只能使用:
    try
    {
    ....CreateFile( "C:\abc.txt" );
    }
    catch( Exception e )
    {
    ....//代码进入这里说明发生错误
    }
    当出错后,只知道它出错了,并不知道是什么原因导致错误。这同【1】。

以及,即使CreateFile是按【4】的规则设计的,但新手在外层是这样使用的:
try
{
....CreateFile( "C:\abc.txt" );
}
catch( Exception e )
{
....//代码进入这里说明发生错误
....throw Exception( "发生错误" )
}
这种情况下,如果这位新手的同事,调用了这段代码,或者用户看到这个错误信息,也只能知道发生了错误,但并不清楚错误的原因。这与【1】是相同的。出于这些原因,新手的同事,以及用户,并没有想到,造成这个问题是原因新手的水平太差,写代码图简单省事。他们却以为是try catch机制不行。因此,这就导致了不建议用try catch。
<script language="JavaScript">
try
{
throw new Error(10,"asdasdasd")
}
catch (e)
{
alert(e.message);
alert(e.description)
alert(e.number)
alert(e.name)
throw new Error(10,"asdasdasd")
}

</script>
在JavaScript可以使用try...catch来进行异常处理。例如:
try {
foo.bar();
} catch (e) {
alert(e.name + ": " + e.message);
}
目前我们可能得到的系统异常主要包含以下6种:
EvalError: raised when an error occurs executing code in eval()
RangeError: raised when a numeric variable or parameter is outside of its valid range
ReferenceError: raised when de-referencing an invalid reference
SyntaxError: raised when a syntax error occurs while parsing code in eval()
TypeError: raised when a variable or parameter is not a valid type
URIError: raised when encodeURI() or decodeURI() are passed invalid parameters
上面的六种异常对象都继承自Error对象。他们都支持以下两种构造方法:
new Error();
new Error("异常信息");
手工抛出异常的方法如下:
try {
throw new Error("Whoops!");
} catch (e) {
alert(e.name + ": " + e.message);
}
如要判断异常信息的类型,可在catch中进行判断:
try {
foo.bar();
} catch (e) {
if (e instanceof EvalError) {
alert(e.name + ":" + e.message);
}
else if (e instanceof RangeError) {
alert(e.name + ": " + e.message);
}
// etc
}
Error具有下面一些主要属性:

description: 错误描述 (仅IE可用).
fileName: 出错的文件名 (仅Mozilla可用).
lineNumber: 出错的行数 (仅Mozilla可用).
message: 错误信息 (在IE下同description)
name: 错误类型.
number: 错误代码 (仅IE可用).
stack: 像Java中的Stack Trace一样的错误堆栈信息 (仅Mozilla可用).
因此为了更好的了解错误信息我们可以将catch部分改为如下形式:
try {
foo.bar();
} catch (e) {
if (browserType != BROWSER_IE) {
alert("name: " + e.name +
"message: " + e.message +
"lineNumber: " + e.lineNumber +
"fileName: " + e.fileName +
"stack: " + e.stack);
}
else {
alert("name: " + e.name +
"errorNumber: " + (e.number & 0xFFFF ) +
"message: " + e.message");
}
}
JavaScript中的throw命令事实上可以抛出任何对象,并且我们可以在catch接受到此对象。例如:
try {
throw new Date(); // 抛出当前时间对象
} catch (e) {
alert(e.toLocaleString()); // 使用本地格式显示当前时间
}

之前一直没有去研究try catch对代码运行的性能影响,只是一直停留在了感觉上,正好最近开会交流学习的时候,有人提出了相关的问题。借着周末,正好研究一番。
window.JSTracker=window.JSTracker||[];
try{
//your code
}catch(e){
JSTracker.push(e);
throwe;//建议将错误再次抛出,避免测试无法发现异常
}
设计实验方式
简单的设计方案也就是对比实验。
空白组1:[无 try catch 的情况下对数据取模1千万次耗时]
<!DOCTYPEhtml>
<html>
<head>
<title>1无trycatch的情况耗时</title>
<script>
!function(){
//无try catch的情况耗时
var t = newDate();
//耗时代码开始
for(vari=0;i<100000000;i++){
varp=i%2;
}
//耗时代码结束
document.write(newDate()–t);
}();
</script>
</head>
<body>
</body>
</html>
参照组2:[将耗时代码用 try 包围,内联耗时代码]
<!DOCTYPEhtml>
<html>
<head>
<title>2在try中内联代码的耗时情况</title>
<script>
!function(){
//在 try 中内联代码的耗时情况
vart=newDate();
try{
//耗时代码开始
for(vari=0;i<100000000;i++){
varp=i%2;
}
//耗时代码结束
thrownewError();
}catch(e){
}
document.write(newDate()–t);
}();
</script>
</head>
<body>
</body>
</html>
参照组3:[将耗时代码用 try 包围,外联耗时代码]
?
<!DOCTYPEhtml>
<html>
<head>
<title>3在try中内联代码的耗时情况</title>
<script>
!function(){
functionrun(){
//耗时代码开始
for(vari=0;i<100000000;i++){
varp=i%2;
}
//耗时代码结束
}
//在 try 中内联代码的耗时情况
vart=newDate();
try{
run();
thrownewError();
}catch(e){
}
document.write(newDate()–t);
}();
</script>
</head>
<body>
</body>
</html>
参照组4:[将耗时代码用 catch 包围,内联耗时代码]
?
<!DOCTYPEhtml>
<html>
<head>
<title>4在catch中内联代码的耗时情况</title>
<script>
!function(){
//在 catch 中内联代码的耗时情况
vart=newDate();
try{
thrownewError();
}catch(e){
//耗时代码开始
for(vari=0;i<100000000;i++){
varp=i%2;
}
//耗时代码结束
}
document.write(newDate()–t);
}();
</script>
</head>
<body>
</body>
</html>
参照组5:[将耗时代码用 catch 包围,外联耗时代码]
?
<!DOCTYPEhtml>
<html>
<head>
<title>5在catch中内联代码的耗时情况</title>
<script>
!function(){
functionrun(){
//耗时代码开始
for(vari=0;i<100000000;i++){
varp=i%2;
}
//耗时代码结束
}
//在 catch 中内联代码的耗时情况
vart=newDate();
try{
thrownewError();
}catch(e){
run();
}
document.write(newDate()–t);
}();
</script>
</head>
<body>
</body>
</html>
运行结果(只选取了 Chrome 作为示例)
– 不使用 TRY-CATCH TRY 中耗时,内联代码 TRY 中耗时,外联代码 CATCH 中耗时,内联代码 CATCH 中耗时,外联代码
Chrome51 98.2 1026.9 107.7 1028.5 105.9
给出总结
使用 try catch 的使用无论是在 try 中的代码还是在 catch 中的代码性能消耗都是一样的。
需要注意的性能消耗在于 try catch 中不要直接塞进去太多的代码(声明太多的变量),最好是吧所有要执行的代码放在另一个 function 中,通过调用这个 function 来执行。
针对第二点,可以查看 ECMA 中关于 try catch 的解释,在代码进入 try catch 的时候 js引擎会拷贝当前的词法环境,拷贝的其实就是当前 scope 下的所有的变量。
建议
在使用 try catch 的时候尽量把 try catch 放在一个相对干净的 scope 中,同时在 try catch 语句中也尽量保证足够少的变量,最好通过函数调用方式来 try catch。
试验中的现象解释
测试过程中还是发现了一个疑问, 以下两段代码在 Chrome 44 中运行出来的结果差距非常大,加了一句空的 try catch 之后平均为:850ms,加上之前为:140ms。
?

!function(){
//无 try catch 的情况耗时
vart=newDate();
//耗时代码开始
for(vari=0;i<100000000;i++){
varp=i%2;
}
//耗时代码结束
document.write(newDate()–t);
try{
}catch(e){
}
}();
!function(){
//无 try catch 的情况耗时
vart=newDate();
//耗时代码开始
for(vari=0;i<100000000;i++){
varp=i%2;
}
//耗时代码结束
document.write(newDate()–t);
}();
其实原因很简单
只要把代码改为这样 耗时就降下来了:
?

!function(){
!function(){
//无 try catch 的情况耗时
vart=newDate();
//耗时代码开始
for(vari=0;i<100000000;i++){
varp=i%2;
}
//耗时代码结束
document.write(newDate()–t);
}();
try{
}catch(e){
}
}();
总结
以上就是关于使用try catch对代码运行性能影响的全部内容。
来源:http://www.jb51.net/article/101291.htm

收起阅读 »

js中的对象object

Native.JS

对象是JavaScript的基本数据类型。
对象是一种复合值:它将很多值(原始值或者其他对象)聚合在一起,可通过名字访问这些值。
对象也可以看做是属性的无序集合,每个属性都是一个名/值对。
属性名是字符串,因此我们可以把对象看成是从字符串到值的映射。
这种基本数据类型还有很多叫法,有些我们已然非常熟悉,比如“散列”(hash)、“散列表”(hashtable)、“字典”(dictionary)、“关联数组”(associativearray)。然而对象不仅仅是字符串到值的映射,除了可以保持自有的属性,JavaScript对象还可以从一个称为原型的对象继承属性。对象的方法通常是继承的属性。这种“原型式继承”(prototypal inheritance)是JavaScript的核心特征。
JavaScript是动态的———可以新增属性也可以删除属性————但他们常用来模拟静态对象以及静态类型语言中的“结构体”(struct)。有时他们也用做字符串的集合(忽略名/值对中的值)。
除了字符串、数字、true、false、null和undefined之外,JavaScript中的值都是对象。尽管字符串、数字和布尔值不是对象,但他们的行为和不可变对象非常类似。
对象最常见的用法是创建(create)、设置(set)、查找(query)、删除(delete)、监测(test)、枚举(enumerate)它的属性。
用术语对三类JavaScript对象和两类属性作区分:
内置对象:是由ECMAScript规范定义的对象或类。例如:数组、函数、日期和正则表达式都是内置对象。
宿主对象:是由JavaScript解释器所嵌入的宿主环境(比如web浏览器)定义的。客户端JavaScript中表示网页结构的HTMLlement对象均是宿主对象。既然宿主环境定义的方法可以当成普通的JavaScript函数对象,那么宿主对象也可以当成内置对象。
自定义对象:是由运行中的JavaScript代码创建的对象。
自有属性:是直接在对象中定义的属性。
继承属性:是在对象在原型对象中定义的属性。

创建对象的方式:
(1)对象直接量:
var book = {
“main title” : “Javascript”,
“sub-title” :“The Definitive Guide”,
author: {
firstname:“David”,
surname:“Flanagan”
}
};
(2)通过new创建对象
var a = new Array();
var d = new Date();
var r = new RegExp(“js”);
(3)原型
每个JavaScript对象(null除外)都和另一个对象相关联。“另一个”对象就是我们熟知的原型,每一个对象都从原型继承属性。
所有通过对象直接量创建的对象都具有同一个原型对象,并可以通过JavaScript代码Object.prototype获得对象原型对象的引用。通过关键字new和构造函数调用创建的对象的原型就是构造函数的prototype属性的值。因此,同使用{ }创建对象一样,通过new Object()创建的对象也继承自Object.prototype,同样,通过new Array()创建的对象的 原型 就是Array.prototype,通过new Date()创建的对象的 原型 就是Date.prototype.
(4) Object.create( )
var o1 = Object.create({x:1,y:2}); //o1继承了属性的x和y
var o3 = Object.create(Object.prototype); // o3和{ }和new Object()一样
//inherit()返回了一个继承自原型对象p的属性的新对象
//这里使用ECMAScript 5中的Object.create()函数
//如果不存在Object.create(),则退化使用其他方法
function inherit(p) {
if(p === null) throw TypeError(); //p是一个对象,但不能是null
if(Object.create) { //如果Object.create()存在
return Object.create(p); //直接使用它
}
var t = typeof p; //否则进行进一步检测
if(t !== "object" && t !== "function"){
throw TypeError();
}
function f() {}; //定义一个空构造函数
f.prototype = p; //将其原属性设置为p
return new f( ); //使用f()创建p的继承对象
}

属性的查询和设置
可以通过(.)或([ ])运算符来获取属性的值。
作为关联数组的对象
var addr = " ";
for (i = 0; i < 4; i ++){
addr += customer["address" + i] + '\n';
}
这段代码读取customer对象的address0、address1、address2、和address3属性,并将他们连接起来。
很多场景只能使用数组写法来完成。假设你正在写一个程序,这个程序利用网络资源就按当期那用户股票市场投资的金额。程序允许用户输入妹纸股票的名称和购股份额。该程序使用名为portfolio的对象来存储这些信息。每只股票这这个对象中都有对相应的属性,属性名就是股票名称,属性值就是购股数量。例如:如果用户持有IBM的50股,那么portifolio属性的值就为50。
部分代码如下:
function addstock(portfolio,stockname,shares){
protfolio[stockname] = shares;
}
当使用for/in循环遍历关联数组时,就可以清晰地体会到for/in的强大之处。下面的例子就是利用for/in计算portfolio的总计值:
function getvalue(portfolio){
var total = 0.0;
for (stock in portfolio){ //遍历portfilio的每只股票
var shares = portfolio[stock]; //得到每只股票的份额
var price = getquote(stock);//查找股票价格
total += shares * price; //将结果累加至total中
}
return total; //返回total值
}
删除属性
delete云算法可以删除对象的属性。
delete只是断开属性和宿主对象的联系,而不会去操作属性中的属性。
delete云算符只能删除自有属性,不能删除继承属性。
当delete表达式删除成功或没有任何副作用(比如删除不存在的属性)时,它返回true。如果delete后不是一个属性访问表达式,delete同样返回true。
o = {x:1}; //o有一个属性x,并继承属性toString
delete o.x; //删除x,返回true
delete o.x; //什么都没有做,x已经不存在了,返回true
delete o.toString //什么也没做,toString是继承来的,对应第三条,返回true
delete 1; //无意义,返回true
delete不能删除那些可配置性为false的属性。
在以下情况下的delete操作会返回false。
delete Object.prototype; //不能删除,属性是不可配置的。
var x = 1; //声明一个全局变量
delete this.x; //不能删除这个属性
function f(){ }; //声明一个全局函数
delete this.f; //也不能删除全局函数
检测属性
JavaScript对象可以看做属性的集合,我们经常会检测集合中成员的所属关系————判断某个属性是否存在于某个对象中。可以通过in运算符、hasOwnPrepperty()和propertyIsEnumerable()方法来完成这个工作,甚至可以仅通过属性查询也可以做到这一点。
in运算符
var o = {x:1};
"x" in o; //true
"y" in o; //false
"toString" in o; //true:o继承toString属性
hasOwnPreperty()用来检测给定的名字是否是对象的自有属性。对于继承属性它将返回false;
o.hasOwnPreperty("x"); //true
o.hasOwnPreperty("y"); //false
o.hasOwnPreperty("toString"); //false,是继承属性
propertyIsEnumerable()是hasOwnProperty()的增强版,只有检测到是自有属性且这个属性的可枚举性(enumerable attribute)为true时它才会返回true。某些内置属性是不可枚举的。
除了使用in运算符之外,另一种更简便的方式是使用 “!==”判断一个属性是否是undefined:
var o = {x:1};
o.x !== undefined; //true
o.y !== undefined; //false
o.toString !== undefined; //true:o继承了toString属性
!包含了null和undefined,用来判断属性是否存在和是否为空,在书中某一章节有使用。

枚举属性
除了检测对象的属性是否存在,我们还经常会遍历对象的属性。通常使用for/in循环遍历,es5中提供了两个更好的替代方案。
for/in循环可以在循环体中遍历对象中所有可枚举的属性(包括自有属性和继承的属性),把属性名赋值给循环变量。对象继承的内置方法不可枚举,但在代码中给对象添加的属性都是可枚举的。
var o = {x:1,y:2,z:3}; //三个可枚举的属性
o.propertyIsEnumerable("toString") //false,不可枚举
for(p in o ) //遍历属性
console.log(p) //输出x,y和z,不会输出toString
例子:用来枚举属性的对象的工具函数
/
把p中的可枚举属性复制到o中,并返回o
如果o和p中含有同名属性,则覆盖o中的属性
这个函数并不处理getter和setter以及复制属性
/
function extend(o,p) {
for (prop in p) { //遍历p中所有属性
o[prop] = p[prop]; //将属性添加至o中
}
return o;
}
/

返回一个新对象,这个对象拥有同时在o和p中出现的属性
如果o和p中有重名属性,使用p中的属性值
*/
function union(o,p){return extend(extend({ }, o),p);}

继承
继承的5种方式要单独一篇文章

对象方法
1.toString()方法
m instanceof Array
toString()方法没有参数,它将返回一个调用这个方法的对象值的字符串。在需要将对象转换为字符串的时候,JavaScript都会调用这个方法。比如,当使用“+”运算符连接一个字符串和一个对象时或者在希望使用字符串的方法使用了对象时都会调用toString()。
var s = {x:1,y:1}.toString(); //"[object Object]"
由于默认的toString()方法并不会输出很多有用的信息,因此很多类都带有自定义的toString()方法。

  1. toLocalString()方法
  2. toJSON()方法
  3. valueOf()方法
    valueOf()方法和toString()方法非常类似,但往往当JavaScript需要将对象转换为某种原始值而字符串的时候才会调用它,尤其是转换为数字的时候。尤其是转换为数字的时候。如果在需要使用原始值得上下文中使用了对象,JavaScript就会自动调用这个方法。默认的valueOf()方法不足为奇,但有些内置类自定义了valueOf()方法(比如Date.valueOf())。
    参考:https://segmentfault.com/a/1190000010661297?_ea=3321402
继续阅读 »

对象是JavaScript的基本数据类型。
对象是一种复合值:它将很多值(原始值或者其他对象)聚合在一起,可通过名字访问这些值。
对象也可以看做是属性的无序集合,每个属性都是一个名/值对。
属性名是字符串,因此我们可以把对象看成是从字符串到值的映射。
这种基本数据类型还有很多叫法,有些我们已然非常熟悉,比如“散列”(hash)、“散列表”(hashtable)、“字典”(dictionary)、“关联数组”(associativearray)。然而对象不仅仅是字符串到值的映射,除了可以保持自有的属性,JavaScript对象还可以从一个称为原型的对象继承属性。对象的方法通常是继承的属性。这种“原型式继承”(prototypal inheritance)是JavaScript的核心特征。
JavaScript是动态的———可以新增属性也可以删除属性————但他们常用来模拟静态对象以及静态类型语言中的“结构体”(struct)。有时他们也用做字符串的集合(忽略名/值对中的值)。
除了字符串、数字、true、false、null和undefined之外,JavaScript中的值都是对象。尽管字符串、数字和布尔值不是对象,但他们的行为和不可变对象非常类似。
对象最常见的用法是创建(create)、设置(set)、查找(query)、删除(delete)、监测(test)、枚举(enumerate)它的属性。
用术语对三类JavaScript对象和两类属性作区分:
内置对象:是由ECMAScript规范定义的对象或类。例如:数组、函数、日期和正则表达式都是内置对象。
宿主对象:是由JavaScript解释器所嵌入的宿主环境(比如web浏览器)定义的。客户端JavaScript中表示网页结构的HTMLlement对象均是宿主对象。既然宿主环境定义的方法可以当成普通的JavaScript函数对象,那么宿主对象也可以当成内置对象。
自定义对象:是由运行中的JavaScript代码创建的对象。
自有属性:是直接在对象中定义的属性。
继承属性:是在对象在原型对象中定义的属性。

创建对象的方式:
(1)对象直接量:
var book = {
“main title” : “Javascript”,
“sub-title” :“The Definitive Guide”,
author: {
firstname:“David”,
surname:“Flanagan”
}
};
(2)通过new创建对象
var a = new Array();
var d = new Date();
var r = new RegExp(“js”);
(3)原型
每个JavaScript对象(null除外)都和另一个对象相关联。“另一个”对象就是我们熟知的原型,每一个对象都从原型继承属性。
所有通过对象直接量创建的对象都具有同一个原型对象,并可以通过JavaScript代码Object.prototype获得对象原型对象的引用。通过关键字new和构造函数调用创建的对象的原型就是构造函数的prototype属性的值。因此,同使用{ }创建对象一样,通过new Object()创建的对象也继承自Object.prototype,同样,通过new Array()创建的对象的 原型 就是Array.prototype,通过new Date()创建的对象的 原型 就是Date.prototype.
(4) Object.create( )
var o1 = Object.create({x:1,y:2}); //o1继承了属性的x和y
var o3 = Object.create(Object.prototype); // o3和{ }和new Object()一样
//inherit()返回了一个继承自原型对象p的属性的新对象
//这里使用ECMAScript 5中的Object.create()函数
//如果不存在Object.create(),则退化使用其他方法
function inherit(p) {
if(p === null) throw TypeError(); //p是一个对象,但不能是null
if(Object.create) { //如果Object.create()存在
return Object.create(p); //直接使用它
}
var t = typeof p; //否则进行进一步检测
if(t !== "object" && t !== "function"){
throw TypeError();
}
function f() {}; //定义一个空构造函数
f.prototype = p; //将其原属性设置为p
return new f( ); //使用f()创建p的继承对象
}

属性的查询和设置
可以通过(.)或([ ])运算符来获取属性的值。
作为关联数组的对象
var addr = " ";
for (i = 0; i < 4; i ++){
addr += customer["address" + i] + '\n';
}
这段代码读取customer对象的address0、address1、address2、和address3属性,并将他们连接起来。
很多场景只能使用数组写法来完成。假设你正在写一个程序,这个程序利用网络资源就按当期那用户股票市场投资的金额。程序允许用户输入妹纸股票的名称和购股份额。该程序使用名为portfolio的对象来存储这些信息。每只股票这这个对象中都有对相应的属性,属性名就是股票名称,属性值就是购股数量。例如:如果用户持有IBM的50股,那么portifolio属性的值就为50。
部分代码如下:
function addstock(portfolio,stockname,shares){
protfolio[stockname] = shares;
}
当使用for/in循环遍历关联数组时,就可以清晰地体会到for/in的强大之处。下面的例子就是利用for/in计算portfolio的总计值:
function getvalue(portfolio){
var total = 0.0;
for (stock in portfolio){ //遍历portfilio的每只股票
var shares = portfolio[stock]; //得到每只股票的份额
var price = getquote(stock);//查找股票价格
total += shares * price; //将结果累加至total中
}
return total; //返回total值
}
删除属性
delete云算法可以删除对象的属性。
delete只是断开属性和宿主对象的联系,而不会去操作属性中的属性。
delete云算符只能删除自有属性,不能删除继承属性。
当delete表达式删除成功或没有任何副作用(比如删除不存在的属性)时,它返回true。如果delete后不是一个属性访问表达式,delete同样返回true。
o = {x:1}; //o有一个属性x,并继承属性toString
delete o.x; //删除x,返回true
delete o.x; //什么都没有做,x已经不存在了,返回true
delete o.toString //什么也没做,toString是继承来的,对应第三条,返回true
delete 1; //无意义,返回true
delete不能删除那些可配置性为false的属性。
在以下情况下的delete操作会返回false。
delete Object.prototype; //不能删除,属性是不可配置的。
var x = 1; //声明一个全局变量
delete this.x; //不能删除这个属性
function f(){ }; //声明一个全局函数
delete this.f; //也不能删除全局函数
检测属性
JavaScript对象可以看做属性的集合,我们经常会检测集合中成员的所属关系————判断某个属性是否存在于某个对象中。可以通过in运算符、hasOwnPrepperty()和propertyIsEnumerable()方法来完成这个工作,甚至可以仅通过属性查询也可以做到这一点。
in运算符
var o = {x:1};
"x" in o; //true
"y" in o; //false
"toString" in o; //true:o继承toString属性
hasOwnPreperty()用来检测给定的名字是否是对象的自有属性。对于继承属性它将返回false;
o.hasOwnPreperty("x"); //true
o.hasOwnPreperty("y"); //false
o.hasOwnPreperty("toString"); //false,是继承属性
propertyIsEnumerable()是hasOwnProperty()的增强版,只有检测到是自有属性且这个属性的可枚举性(enumerable attribute)为true时它才会返回true。某些内置属性是不可枚举的。
除了使用in运算符之外,另一种更简便的方式是使用 “!==”判断一个属性是否是undefined:
var o = {x:1};
o.x !== undefined; //true
o.y !== undefined; //false
o.toString !== undefined; //true:o继承了toString属性
!包含了null和undefined,用来判断属性是否存在和是否为空,在书中某一章节有使用。

枚举属性
除了检测对象的属性是否存在,我们还经常会遍历对象的属性。通常使用for/in循环遍历,es5中提供了两个更好的替代方案。
for/in循环可以在循环体中遍历对象中所有可枚举的属性(包括自有属性和继承的属性),把属性名赋值给循环变量。对象继承的内置方法不可枚举,但在代码中给对象添加的属性都是可枚举的。
var o = {x:1,y:2,z:3}; //三个可枚举的属性
o.propertyIsEnumerable("toString") //false,不可枚举
for(p in o ) //遍历属性
console.log(p) //输出x,y和z,不会输出toString
例子:用来枚举属性的对象的工具函数
/
把p中的可枚举属性复制到o中,并返回o
如果o和p中含有同名属性,则覆盖o中的属性
这个函数并不处理getter和setter以及复制属性
/
function extend(o,p) {
for (prop in p) { //遍历p中所有属性
o[prop] = p[prop]; //将属性添加至o中
}
return o;
}
/

返回一个新对象,这个对象拥有同时在o和p中出现的属性
如果o和p中有重名属性,使用p中的属性值
*/
function union(o,p){return extend(extend({ }, o),p);}

继承
继承的5种方式要单独一篇文章

对象方法
1.toString()方法
m instanceof Array
toString()方法没有参数,它将返回一个调用这个方法的对象值的字符串。在需要将对象转换为字符串的时候,JavaScript都会调用这个方法。比如,当使用“+”运算符连接一个字符串和一个对象时或者在希望使用字符串的方法使用了对象时都会调用toString()。
var s = {x:1,y:1}.toString(); //"[object Object]"
由于默认的toString()方法并不会输出很多有用的信息,因此很多类都带有自定义的toString()方法。

  1. toLocalString()方法
  2. toJSON()方法
  3. valueOf()方法
    valueOf()方法和toString()方法非常类似,但往往当JavaScript需要将对象转换为某种原始值而字符串的时候才会调用它,尤其是转换为数字的时候。尤其是转换为数字的时候。如果在需要使用原始值得上下文中使用了对象,JavaScript就会自动调用这个方法。默认的valueOf()方法不足为奇,但有些内置类自定义了valueOf()方法(比如Date.valueOf())。
    参考:https://segmentfault.com/a/1190000010661297?_ea=3321402
收起阅读 »

js中Array的常用方法

map()方法
map()方法返回一个新数组,数组中的元素为原始数组元素调用函数后处理的值
注意:1.map()不会对空数组进行检测,2.map()不会改变原始数组。
var numbers = [4, 9, 16, 25];
numbers.map(Math.sqrt); //[2,3,4,5]

concat() 连接两个或更多的数组,并返回结果
copyWithin() 从数组的指定位置拷贝元素到数组的另一个指定位置中
every() 检测数值元素的每个元素是否都符合条件
fill() 使用一个固定值来填充数组
filter() 检测数值元素,并返回符合条件所有元素的数组
find() 返回符合传入测试(函数)条件的数组元素
findindex() 返回符合传入测试(函数)条件的数组元素索引
forEach() 数组每个元素都执行一次回调函数
includes() 判断一个数组是否包含一个指定的值
indexOf() 搜索数组中的元素,并返回它所在的位置
join() 把数组的所有元素放入一个字符串
lastIndexOf() 返回一个指定的字符串值最后出现的位置,在一个字符串中的指定位置从后向前搜索
map( ) 通过指定函数处理数组的每个元素,并返回处理后的数组。map()方法定义在js的Array中,
传入自己的函数,得到一个新的Array作为结果:

function pow(x) {  
      return x * x;  
}  
var arr = [1,2,3,4,5,6,7,8,9];  
app.map(pow);//[1, 4, 9, 16, 25, 36, 49, 64, 81]  
map()传入的参数是pow,即函数对象本身  
map()作为高阶函数,可以计算任意复杂的函数,比如:把Array的所有数字转为字符串:  
var  arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];  
arr.map(String); // ['1', '2', '3', '4', '5', '6', '7', '8', '9'];  
只需要一行代码

pop()删除数组的最后一个元素并返回删除的元素
push()向数组的末尾添加一个或更多元素,并返回新的长度。
reduce()将数组元素计算为一个值(从左到右),这个函数必须接收两个参数。猜测此方法的内部实现为:当前返回的值作为参数参与到下一次函数的计算中,从而从左往右执行。

对array求和:  
var arr = [1,3,5,7,9];  
arr.reduce(function (x,y){  
    return x + y;  
}); //25  
求积  
arr.reduce(function (x,y) {  
     return parseInt(x) * parseInt(y);  
});

reduceRight()将数组元素计算为一个值(从右到左)
reverse() 反转数组的元素顺序
shift() 删除并返回数组的第一个元素
slice() 选取数组的一部分,并返回一个新数组
some()检测数组元素中是否有元素符合指定条件
sort() 对数组的元素进行排序
splice()从数组中添加或删除元素
toString()把数组转换为字符串,并返回结果
unshift() 想数组的开头添加一个或多个元素,并返回新的长度
valueOf ( ) 返回数组对象的原始值

ES6新增方法
Array.from()用于将两类对象转为真正的数组:类似数组的对象(array-like-object)和可变遍历(itreable)的对象(包括ES6新增的数据结构Set和Map)。
例如,如下类数组:

let arrayLIke = {  
    '0' : 'a',  
    '1' : 'b',  
    '2' : 'c',  
    length: 3  
}  
//ES5的写法  
var arr1 = [ ].slice.call(arrayLike); // ['a','b','c']  
//ES6的写法  
let arr2 = Array.from(arrayLike); //['a','b','c']

数组对象自带length属性(下图为Array的length):

参考链接:https://www.cnblogs.com/jiangyi666/p/5991324.html

继续阅读 »

map()方法
map()方法返回一个新数组,数组中的元素为原始数组元素调用函数后处理的值
注意:1.map()不会对空数组进行检测,2.map()不会改变原始数组。
var numbers = [4, 9, 16, 25];
numbers.map(Math.sqrt); //[2,3,4,5]

concat() 连接两个或更多的数组,并返回结果
copyWithin() 从数组的指定位置拷贝元素到数组的另一个指定位置中
every() 检测数值元素的每个元素是否都符合条件
fill() 使用一个固定值来填充数组
filter() 检测数值元素,并返回符合条件所有元素的数组
find() 返回符合传入测试(函数)条件的数组元素
findindex() 返回符合传入测试(函数)条件的数组元素索引
forEach() 数组每个元素都执行一次回调函数
includes() 判断一个数组是否包含一个指定的值
indexOf() 搜索数组中的元素,并返回它所在的位置
join() 把数组的所有元素放入一个字符串
lastIndexOf() 返回一个指定的字符串值最后出现的位置,在一个字符串中的指定位置从后向前搜索
map( ) 通过指定函数处理数组的每个元素,并返回处理后的数组。map()方法定义在js的Array中,
传入自己的函数,得到一个新的Array作为结果:

function pow(x) {  
      return x * x;  
}  
var arr = [1,2,3,4,5,6,7,8,9];  
app.map(pow);//[1, 4, 9, 16, 25, 36, 49, 64, 81]  
map()传入的参数是pow,即函数对象本身  
map()作为高阶函数,可以计算任意复杂的函数,比如:把Array的所有数字转为字符串:  
var  arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];  
arr.map(String); // ['1', '2', '3', '4', '5', '6', '7', '8', '9'];  
只需要一行代码

pop()删除数组的最后一个元素并返回删除的元素
push()向数组的末尾添加一个或更多元素,并返回新的长度。
reduce()将数组元素计算为一个值(从左到右),这个函数必须接收两个参数。猜测此方法的内部实现为:当前返回的值作为参数参与到下一次函数的计算中,从而从左往右执行。

对array求和:  
var arr = [1,3,5,7,9];  
arr.reduce(function (x,y){  
    return x + y;  
}); //25  
求积  
arr.reduce(function (x,y) {  
     return parseInt(x) * parseInt(y);  
});

reduceRight()将数组元素计算为一个值(从右到左)
reverse() 反转数组的元素顺序
shift() 删除并返回数组的第一个元素
slice() 选取数组的一部分,并返回一个新数组
some()检测数组元素中是否有元素符合指定条件
sort() 对数组的元素进行排序
splice()从数组中添加或删除元素
toString()把数组转换为字符串,并返回结果
unshift() 想数组的开头添加一个或多个元素,并返回新的长度
valueOf ( ) 返回数组对象的原始值

ES6新增方法
Array.from()用于将两类对象转为真正的数组:类似数组的对象(array-like-object)和可变遍历(itreable)的对象(包括ES6新增的数据结构Set和Map)。
例如,如下类数组:

let arrayLIke = {  
    '0' : 'a',  
    '1' : 'b',  
    '2' : 'c',  
    length: 3  
}  
//ES5的写法  
var arr1 = [ ].slice.call(arrayLike); // ['a','b','c']  
//ES6的写法  
let arr2 = Array.from(arrayLike); //['a','b','c']

数组对象自带length属性(下图为Array的length):

参考链接:https://www.cnblogs.com/jiangyi666/p/5991324.html

收起阅读 »

js中Object常用方法和属性

Object的常用方法和属性也是很常用的。
一、属性
Object自带一个prototype的属性,即Object.prototype, Object.prototype本身也只是一个对象,也会有一些属性和方法。
1.属性

Object.prototype.writable:默认为false   
Object.prototype.enumerable:默认为false  
Object.prototype.configurable:默认为false   
Object.prototype.constructor:用于创建一个对象的原型。 

2、常用方法

Object.prototype.hasOwnProperty():返回一个布尔值,表示某个对象是否含有指定的属性,而且此属性非原型链继承。  
Object.prototype.isPrototypeOf():返回一个布尔值,表示指定的对象是否在本对象的原型链中。  
Object.prototype.propertyIsEnumerable():判断指定属性是否可枚举。  

Object.prototype.toString():返回对象的字符串表示。  

Object.prototype.watch():给对象的某个属性增加监听。  

Object.prototype.unwatch():移除对象某个属性的监听。  

Object.prototype.valueOf():返回指定对象的原始值。

二、方法

Object.assign(target,...sources):把任意多的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。  
Object.create(proto,[propertiesobject]):创建一个拥有指定原型和若干个指定属性的对象。  
Object.defineProperty(obj,prop,descriptor):直接在一个对象上定义一个新属性,或者修改一个已经存在的属性,并返回这个对象。obj:需要定义属性的对象。prop:需定义或修改的属性的名字。descriptor:将被定义或修改的属性的描述符。  
Object.entries(obj):返回一个包含由给定对象所有可枚举属性的属性名和属性值组成的 [属性名,属性值] 键值对的数组,数组中键值对的排列顺序和使用for…in循环遍历该对象时返回的顺序一致。   
举例:   
var obj = { foo: “bar”, baz: 42 };   
console.log(Object.entries(obj)); // [ [‘foo’, ‘bar’], [‘baz’, 42] ]  
Object.freeze(obj):冻结一个对象,冻结指的是不能向这个对象添加新的属性,不能修改其已有属性的值,不能删除已有属性,以及不能修改该对象已有属性的可枚举性、可配置性、可写性。也就是说,这个对象永远是不可变的。该方法返回被冻结的对象。  
Object.getOwnPropertyDescriptor(obj, prop):返回指定对象上一个自有属性对应的属性描述符。  
Object.getOwnPropertyNames(obj):返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性)组成的数组。 

举例:
// 类数组对象

var obj = { 0: “a”, 1: “b”, 2: “c”};   
console.log(Object.getOwnPropertyNames(obj).sort()); // [“0”, “1”, “2”]  
Object.getPrototypeOf(object):返回该对象的原型。  
Object.is(value1, value2):判断两个值是否是同一个值。  
Object.isExtensible(obj):判断一个对象是否是可扩展的(是否可以在它上面添加新的属性)。  
Object.isFrozen(obj):判断一个对象是否被冻结(frozen)。  
Object.isSealed(obj):判断一个对象是否是密封的(sealed)。密封对象是指那些不可 扩展 的,且所有自身属性都不可配置的(non-configurable)且属性不可删除的对象(其可以是可写的)。  
Object.keys(obj):返回一个由给定对象的所有可枚举自身属性的属性名组成的数组,数组中属性名的排列顺序和使用for-in循环遍历该对象时返回的顺序一致 

举例:

var arr = [“a”, “b”, “c”];   
alert(Object.keys(arr)); // 弹出”0,1,2”  
// 类数组对象   
var obj = { 0 : “a”, 1 : “b”, 2 : “c”};   
alert(Object.keys(obj)); // 弹出”0,1,2”  
Object.preventExtensions(obj):让一个对象变的不可扩展,也就是永远不能再添加新的属性。  
Object.setPrototypeOf(obj, prototype):将一个指定的对象的原型设置为另一个对象或者null  
Object.values(obj):返回一个包含指定对象所有的可枚举属性值的数组,数组中的值顺序和使用for…in循环遍历的顺序一样。   

举例:   
var obj = { foo: “bar”, baz: 42 };   
console.log(Object.values(obj)); // [‘bar’, 42]  

Object.assign()与深拷贝
深拷贝与浅拷贝
所谓深拷贝与浅拷贝,是围绕引用类型变量的拷贝进行的讨论。
在ecmascript中,变量分为基本类型和引用类型两种。其本质区别是不可变性,基本类型是不可变的,而引用类型是可变的。
所谓基本类型的不可变性,我们可以举个例子

let a = 1;  
let b = 1;  
a++;  
a===2;//true  
b===1;//true

声明一个变量a,并为其赋值1,这时内存中开辟出一片区域用来储存1。此时声明了一个变量b,也为b赋值1。当执行a++时,
基本类型的不可变性就体现出来,a++的值应该为2,但是这个值并不会将原来储存1的那片内存覆盖掉,而是再开辟一片内
存来存储2。所以对于这个1来讲,他是永远不可变的。
而对于引用变量则不同,因为其存储的是只想某个内存区域的地址,所以其修改时直接操作在内存上的,
这就导致了深拷贝和浅拷贝问题的出现。

浅拷贝  

let foo = {  
   x: 1,  
   y: -1  
}  
let bar = foo;  
foo.x++;  
foo.x ===2 //true  
bar.x ===2 //true

这就是最简单的浅拷贝,其效果十分明显,对拷贝源的操作,会直接体现在拷贝目标上,因为这个赋值行为的本质是内存地址的赋值,
所以他们指向了同一片内存区域。
浅拷贝十分容易,也十分常见,但却无法满足需求,加入我们需要获得拷贝源完全相同,却又不会互相影响的对象,应该怎么办呢
Object.assign()
ES6为我们提供了一种十分好用的方法,Object.assign(target, ...source)方法
assign方法接受多个参数,第一个参数target为拷贝目标,剩余参数...source是拷贝源。此方法可以将...source中的属性复制到target中,
同名属性会进行覆盖,并且在复制过程中实现了'伪'深拷贝

let foo = {  
   a: 1,  
  b: 2,  
 c: {  
   d: 1   
   }  
}  
let bar =  { };  
Object.assign(bar, foo);  
foo.a++;  
foo.a === 2 //true  
bar.a === 1 //true  
乍一看,好像已经实现了深拷贝的效果,对foo.a进行的操作并没有体现在bar.a中,但是再往后看  
foo.c.d++;  
foo.c.d === 2 //true  
bar.c.d === 1 //false  
bar.c.d === 2 //true

Object.assign()的拷贝类型十分明显了,这是一种可以对非嵌套对象进行深拷贝的方法,如果对象中出现嵌套情况,那么其对被嵌套对象的行为就成了普通的浅拷贝.

如果真的想进行深拷贝,最简单粗暴地方式就是JSON操作。
JSON对象中包含两个方法,stringify( )和parse( ),前者可以将对象JSON化,而后者可以将JSON格式转换为对象,这是一种可以实现深拷贝的方法。
但这种方法的缺陷是会破坏原型链,并且无法拷贝属性值为function的属性
所以如果只是想单纯复制一个嵌套对象,可以使用此方法

let foo =  {  
     a:1,  
     b: {  
          c: 1  
   }  
}  
let bar = JSON.parse(JSON.stringify(foo));

来源:https://segmentfault.com/a/1190000010661297

es6入门
Object.assign方法用于对象的合并
Object.assign方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)。
const target = { a: 1 };

const source1 = { b: 2 };
const source2 = { c: 3 };

Object.assign(target, source1, source2);

target // {a:1, b:2, c:3}
如果只有一个参数,Object.assign会直接返回该参数。
const obj = {a:1};
Object.assign(obj) === obj //true
如果该参数不是对象,则会先转成对象,然后返回。
typeof Object.assign(2) // "object"
由于undefined和null无法转成对象,所以如果它们作为参数,就会报错。
Object.assign(undefined) // 报错
Object.assign(null) // 报错
如果非对象参数出现在源对象的位置(即非首参数),那么处理规则有所不同。首先,这些参数都会转成对象,如果无法转成对象,就会跳过。这意味着,如果undefined和null不在首参数,就不会报错。
let obj = {a: 1};
Object.assign(obj, undefined) === obj // true
Object.assign(obj, null) === obj // true
其他类型的值(即数值、字符串和布尔值)不在首参数,也不会报错。但是,除了字符串会以数组形式,拷贝入目标对象,其他值都不会产生效果。
const v1 = 'abc';
const v2 = true;
const v3 = 10;

const obj = Object.assign({}, v1, v2, v3);
console.log(obj); // { "0": "a", "1": "b", "2": "c" }
Object.assign拷贝的属性是有限制的,只拷贝源对象的自身属性(不拷贝继承属性),也不拷贝不可枚举属性。
Object.assign({b: 'c'},
Object.defineProperty({},'invisible',{
enumerable: false,
value: 'hello'
})
}
//{b: 'c'}
注意点
(1)浅拷贝
Object.assign方法实行的是浅拷贝,而不是深拷贝。也就是说,如果源对象某个属性的值是对象,那么目标对象拷贝得到的是这个对象的引用。
const obj1 = {a: {b: 1}};
const obj2 = Object.assign({},obj1);
obj1.a.b = 2;
obj2.a.b //2
上面代码中,源对象obj1的a属性的值是一个对象,Object.assign拷贝得到的是这个对象的引用。这个对象的任何变化,都会反映到目标对象上面。

const target = { a: 1 };  

const source1 = { b: 2 };  
const source2 = { c: 3 };  

Object.assign(target, source1, source2);  
target // {a:1, b:2, c:3}  
source1.b={m:6}  
Object.assign(target,source1,source2)  

target ={a: 1, b:{m:6}, c: 3}  
target.b.m = 8; //修改m的值  
source1.b.m //8 说明是浅拷贝  
原因分析:string、boolean、number是按值传递的,array、obj是按引用传递。

(2)同名属性的替换
对于这种嵌套的对象,一旦遇到同名属性,Object.assign的处理方法是替换,而不是添加。

const target = { a: { b: 'c', d: 'e' } }  
const source = { a: { b: 'hello' } }  
Object.assign(target, source)  
// { a: { b: 'hello' } }

上面代码中,target对象的a属性被source对象的a属性整个替换掉了,而不会得到{ a: { b: 'hello', d: 'e' } }的结果。
这通常不是开发者想要的,需要特别小心。
一些函数库提供Object.assign的定制版本(比如 Lodash 的_.defaultsDeep方法),可以得到深拷贝的合并。
jquery的$.extend(true,{})可以进行深层合并

常见用途
Object.assign方法有很多用处
(1)为对象添加属性

class Point {  
   constructor (x,y) {  
       Object.assign(this,{x,y});  
   }  
}  
const source1 = {b:2};  
const source2 = {c:3};  

(2)为对象添加方法

Object.assign(SomeClass.prototype,{  
   someMethod(arg1,arg2) {  
  },  
  anotherMethod( ) {  
  }  
})  
//等同于  
SomeClass.prototype.someMethod = function (arg1,arg2) {  

};  
SomeClass.prototype.anotherMethod = function ( ) {   

};

上面代码使用了对象属性的简洁表示法,直接将两个函数放在大括号中,
再使用assign方法添加到SomeClass.prototype之中。

(3)克隆对象

function clone(origin) {  
    return Object.assign({},origin);  
}  
上面代码将原始对象拷贝到一个空对象,就得到了原始对象的克隆。  
不过,用这种方法克隆,只能克隆原始对象自身的值(也就是浅拷贝),不能克隆它继承的值。如果想要保持继承链,可以采用下面的代码。  
function clone(origin) {  
    let originProto = Object.getPrototypeOf(origin);  
    return Object.assign(Object.create(originProto),origin);  
}

(4)合并多个对象
将多个对象合并到某个对象。
const merge = (target,...sources) => Object.assign(target,...sources);
如果希望合并后返回一个新对象,可以改写上面函数,对一个空对象合并。
const merge = (...sources) => Object.assign({},...sources);
(5)为属性指定默认值

const DEFAULTS = {  
    logLevel: 0,  
    outputFormat: 'html'  
};  
function processContent(options) {  
    options = Object.assign({},DEFAULTS,options);  
    console.log(options);  
}

上面代码中,DEFAULTS对象是默认值,options对象是用户提供的参数。Object.assign方法将DEFAULTS和options合并成一个新对象,如果两者有同名属性,则option的属性值会覆盖DEFAULTS的属性值。
注意,由于存在浅拷贝的问题,DEFAULTS对象和options对象的所有属性的值,最好都是简单类型,不要指向另一个对象。否则,DEFAULTS对象的该属性很可能不起作用。
声明对象的时候,为了防止,声明的对象被覆盖,一般用const声明。

const DEFAULTS = {  
    url: {  
        host: 'example.com',  
        port: 7070  
    }  
}  
processContent({url:{port: 8080}})  
//{ url: {port: 8000}}

上面代码的愿意是将url.port改成8000,url.host不变。实际结果却是options.url覆盖掉DEFAULTS.url,所以url.host就不存在了。
我的理解是,非嵌套类型,假如你这里说的是值类型。
值类型放在堆栈中,是只会存在一份的,谁用归谁。
引用类型,放在堆中,这时候,浅拷贝是拷贝了一份引用放在堆栈,深拷贝是拷贝了对象本身放在堆。

如何实现一个对象的深拷贝?

http://www.jb51.net/article/99013.htm

继续阅读 »

Object的常用方法和属性也是很常用的。
一、属性
Object自带一个prototype的属性,即Object.prototype, Object.prototype本身也只是一个对象,也会有一些属性和方法。
1.属性

Object.prototype.writable:默认为false   
Object.prototype.enumerable:默认为false  
Object.prototype.configurable:默认为false   
Object.prototype.constructor:用于创建一个对象的原型。 

2、常用方法

Object.prototype.hasOwnProperty():返回一个布尔值,表示某个对象是否含有指定的属性,而且此属性非原型链继承。  
Object.prototype.isPrototypeOf():返回一个布尔值,表示指定的对象是否在本对象的原型链中。  
Object.prototype.propertyIsEnumerable():判断指定属性是否可枚举。  

Object.prototype.toString():返回对象的字符串表示。  

Object.prototype.watch():给对象的某个属性增加监听。  

Object.prototype.unwatch():移除对象某个属性的监听。  

Object.prototype.valueOf():返回指定对象的原始值。

二、方法

Object.assign(target,...sources):把任意多的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。  
Object.create(proto,[propertiesobject]):创建一个拥有指定原型和若干个指定属性的对象。  
Object.defineProperty(obj,prop,descriptor):直接在一个对象上定义一个新属性,或者修改一个已经存在的属性,并返回这个对象。obj:需要定义属性的对象。prop:需定义或修改的属性的名字。descriptor:将被定义或修改的属性的描述符。  
Object.entries(obj):返回一个包含由给定对象所有可枚举属性的属性名和属性值组成的 [属性名,属性值] 键值对的数组,数组中键值对的排列顺序和使用for…in循环遍历该对象时返回的顺序一致。   
举例:   
var obj = { foo: “bar”, baz: 42 };   
console.log(Object.entries(obj)); // [ [‘foo’, ‘bar’], [‘baz’, 42] ]  
Object.freeze(obj):冻结一个对象,冻结指的是不能向这个对象添加新的属性,不能修改其已有属性的值,不能删除已有属性,以及不能修改该对象已有属性的可枚举性、可配置性、可写性。也就是说,这个对象永远是不可变的。该方法返回被冻结的对象。  
Object.getOwnPropertyDescriptor(obj, prop):返回指定对象上一个自有属性对应的属性描述符。  
Object.getOwnPropertyNames(obj):返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性)组成的数组。 

举例:
// 类数组对象

var obj = { 0: “a”, 1: “b”, 2: “c”};   
console.log(Object.getOwnPropertyNames(obj).sort()); // [“0”, “1”, “2”]  
Object.getPrototypeOf(object):返回该对象的原型。  
Object.is(value1, value2):判断两个值是否是同一个值。  
Object.isExtensible(obj):判断一个对象是否是可扩展的(是否可以在它上面添加新的属性)。  
Object.isFrozen(obj):判断一个对象是否被冻结(frozen)。  
Object.isSealed(obj):判断一个对象是否是密封的(sealed)。密封对象是指那些不可 扩展 的,且所有自身属性都不可配置的(non-configurable)且属性不可删除的对象(其可以是可写的)。  
Object.keys(obj):返回一个由给定对象的所有可枚举自身属性的属性名组成的数组,数组中属性名的排列顺序和使用for-in循环遍历该对象时返回的顺序一致 

举例:

var arr = [“a”, “b”, “c”];   
alert(Object.keys(arr)); // 弹出”0,1,2”  
// 类数组对象   
var obj = { 0 : “a”, 1 : “b”, 2 : “c”};   
alert(Object.keys(obj)); // 弹出”0,1,2”  
Object.preventExtensions(obj):让一个对象变的不可扩展,也就是永远不能再添加新的属性。  
Object.setPrototypeOf(obj, prototype):将一个指定的对象的原型设置为另一个对象或者null  
Object.values(obj):返回一个包含指定对象所有的可枚举属性值的数组,数组中的值顺序和使用for…in循环遍历的顺序一样。   

举例:   
var obj = { foo: “bar”, baz: 42 };   
console.log(Object.values(obj)); // [‘bar’, 42]  

Object.assign()与深拷贝
深拷贝与浅拷贝
所谓深拷贝与浅拷贝,是围绕引用类型变量的拷贝进行的讨论。
在ecmascript中,变量分为基本类型和引用类型两种。其本质区别是不可变性,基本类型是不可变的,而引用类型是可变的。
所谓基本类型的不可变性,我们可以举个例子

let a = 1;  
let b = 1;  
a++;  
a===2;//true  
b===1;//true

声明一个变量a,并为其赋值1,这时内存中开辟出一片区域用来储存1。此时声明了一个变量b,也为b赋值1。当执行a++时,
基本类型的不可变性就体现出来,a++的值应该为2,但是这个值并不会将原来储存1的那片内存覆盖掉,而是再开辟一片内
存来存储2。所以对于这个1来讲,他是永远不可变的。
而对于引用变量则不同,因为其存储的是只想某个内存区域的地址,所以其修改时直接操作在内存上的,
这就导致了深拷贝和浅拷贝问题的出现。

浅拷贝  

let foo = {  
   x: 1,  
   y: -1  
}  
let bar = foo;  
foo.x++;  
foo.x ===2 //true  
bar.x ===2 //true

这就是最简单的浅拷贝,其效果十分明显,对拷贝源的操作,会直接体现在拷贝目标上,因为这个赋值行为的本质是内存地址的赋值,
所以他们指向了同一片内存区域。
浅拷贝十分容易,也十分常见,但却无法满足需求,加入我们需要获得拷贝源完全相同,却又不会互相影响的对象,应该怎么办呢
Object.assign()
ES6为我们提供了一种十分好用的方法,Object.assign(target, ...source)方法
assign方法接受多个参数,第一个参数target为拷贝目标,剩余参数...source是拷贝源。此方法可以将...source中的属性复制到target中,
同名属性会进行覆盖,并且在复制过程中实现了'伪'深拷贝

let foo = {  
   a: 1,  
  b: 2,  
 c: {  
   d: 1   
   }  
}  
let bar =  { };  
Object.assign(bar, foo);  
foo.a++;  
foo.a === 2 //true  
bar.a === 1 //true  
乍一看,好像已经实现了深拷贝的效果,对foo.a进行的操作并没有体现在bar.a中,但是再往后看  
foo.c.d++;  
foo.c.d === 2 //true  
bar.c.d === 1 //false  
bar.c.d === 2 //true

Object.assign()的拷贝类型十分明显了,这是一种可以对非嵌套对象进行深拷贝的方法,如果对象中出现嵌套情况,那么其对被嵌套对象的行为就成了普通的浅拷贝.

如果真的想进行深拷贝,最简单粗暴地方式就是JSON操作。
JSON对象中包含两个方法,stringify( )和parse( ),前者可以将对象JSON化,而后者可以将JSON格式转换为对象,这是一种可以实现深拷贝的方法。
但这种方法的缺陷是会破坏原型链,并且无法拷贝属性值为function的属性
所以如果只是想单纯复制一个嵌套对象,可以使用此方法

let foo =  {  
     a:1,  
     b: {  
          c: 1  
   }  
}  
let bar = JSON.parse(JSON.stringify(foo));

来源:https://segmentfault.com/a/1190000010661297

es6入门
Object.assign方法用于对象的合并
Object.assign方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)。
const target = { a: 1 };

const source1 = { b: 2 };
const source2 = { c: 3 };

Object.assign(target, source1, source2);

target // {a:1, b:2, c:3}
如果只有一个参数,Object.assign会直接返回该参数。
const obj = {a:1};
Object.assign(obj) === obj //true
如果该参数不是对象,则会先转成对象,然后返回。
typeof Object.assign(2) // "object"
由于undefined和null无法转成对象,所以如果它们作为参数,就会报错。
Object.assign(undefined) // 报错
Object.assign(null) // 报错
如果非对象参数出现在源对象的位置(即非首参数),那么处理规则有所不同。首先,这些参数都会转成对象,如果无法转成对象,就会跳过。这意味着,如果undefined和null不在首参数,就不会报错。
let obj = {a: 1};
Object.assign(obj, undefined) === obj // true
Object.assign(obj, null) === obj // true
其他类型的值(即数值、字符串和布尔值)不在首参数,也不会报错。但是,除了字符串会以数组形式,拷贝入目标对象,其他值都不会产生效果。
const v1 = 'abc';
const v2 = true;
const v3 = 10;

const obj = Object.assign({}, v1, v2, v3);
console.log(obj); // { "0": "a", "1": "b", "2": "c" }
Object.assign拷贝的属性是有限制的,只拷贝源对象的自身属性(不拷贝继承属性),也不拷贝不可枚举属性。
Object.assign({b: 'c'},
Object.defineProperty({},'invisible',{
enumerable: false,
value: 'hello'
})
}
//{b: 'c'}
注意点
(1)浅拷贝
Object.assign方法实行的是浅拷贝,而不是深拷贝。也就是说,如果源对象某个属性的值是对象,那么目标对象拷贝得到的是这个对象的引用。
const obj1 = {a: {b: 1}};
const obj2 = Object.assign({},obj1);
obj1.a.b = 2;
obj2.a.b //2
上面代码中,源对象obj1的a属性的值是一个对象,Object.assign拷贝得到的是这个对象的引用。这个对象的任何变化,都会反映到目标对象上面。

const target = { a: 1 };  

const source1 = { b: 2 };  
const source2 = { c: 3 };  

Object.assign(target, source1, source2);  
target // {a:1, b:2, c:3}  
source1.b={m:6}  
Object.assign(target,source1,source2)  

target ={a: 1, b:{m:6}, c: 3}  
target.b.m = 8; //修改m的值  
source1.b.m //8 说明是浅拷贝  
原因分析:string、boolean、number是按值传递的,array、obj是按引用传递。

(2)同名属性的替换
对于这种嵌套的对象,一旦遇到同名属性,Object.assign的处理方法是替换,而不是添加。

const target = { a: { b: 'c', d: 'e' } }  
const source = { a: { b: 'hello' } }  
Object.assign(target, source)  
// { a: { b: 'hello' } }

上面代码中,target对象的a属性被source对象的a属性整个替换掉了,而不会得到{ a: { b: 'hello', d: 'e' } }的结果。
这通常不是开发者想要的,需要特别小心。
一些函数库提供Object.assign的定制版本(比如 Lodash 的_.defaultsDeep方法),可以得到深拷贝的合并。
jquery的$.extend(true,{})可以进行深层合并

常见用途
Object.assign方法有很多用处
(1)为对象添加属性

class Point {  
   constructor (x,y) {  
       Object.assign(this,{x,y});  
   }  
}  
const source1 = {b:2};  
const source2 = {c:3};  

(2)为对象添加方法

Object.assign(SomeClass.prototype,{  
   someMethod(arg1,arg2) {  
  },  
  anotherMethod( ) {  
  }  
})  
//等同于  
SomeClass.prototype.someMethod = function (arg1,arg2) {  

};  
SomeClass.prototype.anotherMethod = function ( ) {   

};

上面代码使用了对象属性的简洁表示法,直接将两个函数放在大括号中,
再使用assign方法添加到SomeClass.prototype之中。

(3)克隆对象

function clone(origin) {  
    return Object.assign({},origin);  
}  
上面代码将原始对象拷贝到一个空对象,就得到了原始对象的克隆。  
不过,用这种方法克隆,只能克隆原始对象自身的值(也就是浅拷贝),不能克隆它继承的值。如果想要保持继承链,可以采用下面的代码。  
function clone(origin) {  
    let originProto = Object.getPrototypeOf(origin);  
    return Object.assign(Object.create(originProto),origin);  
}

(4)合并多个对象
将多个对象合并到某个对象。
const merge = (target,...sources) => Object.assign(target,...sources);
如果希望合并后返回一个新对象,可以改写上面函数,对一个空对象合并。
const merge = (...sources) => Object.assign({},...sources);
(5)为属性指定默认值

const DEFAULTS = {  
    logLevel: 0,  
    outputFormat: 'html'  
};  
function processContent(options) {  
    options = Object.assign({},DEFAULTS,options);  
    console.log(options);  
}

上面代码中,DEFAULTS对象是默认值,options对象是用户提供的参数。Object.assign方法将DEFAULTS和options合并成一个新对象,如果两者有同名属性,则option的属性值会覆盖DEFAULTS的属性值。
注意,由于存在浅拷贝的问题,DEFAULTS对象和options对象的所有属性的值,最好都是简单类型,不要指向另一个对象。否则,DEFAULTS对象的该属性很可能不起作用。
声明对象的时候,为了防止,声明的对象被覆盖,一般用const声明。

const DEFAULTS = {  
    url: {  
        host: 'example.com',  
        port: 7070  
    }  
}  
processContent({url:{port: 8080}})  
//{ url: {port: 8000}}

上面代码的愿意是将url.port改成8000,url.host不变。实际结果却是options.url覆盖掉DEFAULTS.url,所以url.host就不存在了。
我的理解是,非嵌套类型,假如你这里说的是值类型。
值类型放在堆栈中,是只会存在一份的,谁用归谁。
引用类型,放在堆中,这时候,浅拷贝是拷贝了一份引用放在堆栈,深拷贝是拷贝了对象本身放在堆。

如何实现一个对象的深拷贝?

http://www.jb51.net/article/99013.htm

收起阅读 »

分享一个带登录的 demo,解决了双webview导航启动时,首页加载卡顿的问题

研究了好久的首页启动白屏卡顿现象,虽然只有那么短短的几毫秒,但是看着总是不舒服,尝试了官方推荐的nativeTab_demo。

但是不想用那个小凸点的半圆球,改了好久都没有解决问题,无奈还是选用了这个,然后就在官方的代码上继续修改,终于找到突破口,略微修改了一下代码,实现了启动的时候 加载首页可以像nativeTab_demo那么迅速显示,不卡顿,而且可以少加载一个页面,直接加载的首页可以显示,不用再定义main页面。

所以也想分享给各位在坑中挣扎的兄弟们。这个是我准备给公司开发的APP用的,就直接分享基础版的东西给你们吧。代码写的不好,大神勿喷。

demo在下面,自己下载吧!

 mui.init();  

    /**此处是有四个菜单,首页不需要在这里创建,默认访问的首页会自动创建出来,所以tuan.html算第二个菜单开始**/  
    var subPages = ['tuan.html','small.html','person.html'];  
    mui.plusReady(function(){  

        //创建主体和子页面  
        var sub0 = plus.webview.getWebviewById(subPages[0]);  
        var sub1 = plus.webview.getWebviewById(subPages[1]);  
        var sub2 = plus.webview.getWebviewById(subPages[2]);  
/**此处保证页面只有第一次运行的时候去创建那几个子页面**/  
        if(!sub0 || !sub1 || !sub2){  
            var self = plus.webview.currentWebview();  
            for(var i=0;i<3;i++){  
                var sub = plus.webview.create(  
                subPages[i],  
                subPages[i],  
                {  
                    top:'0px',  
                    bottom:'51px'  
                });  
            }  
/**此处不写hide()方法也是可以的,运行时,其他页面创建出来后都在首页后面被遮住了好像,具体我也没懂**/  
        }  
        mui(".mui-bar-tab").on("tap","a",function(e){  
            var tagPage = this.getAttribute("href");  
/**此处如果是点击首页,就隐藏其他页面,就会显示首页了**/  
            if(tagPage == 'index.html'){  
                for(var j=0;j<3;j++){  
                    plus.webview.getWebviewById(subPages[j]).hide();  
                }  
            }else{  
                plus.webview.show(tagPage,"fade-in",300);  
            }  
        })  

    });

写的不好,多多指教!还有一个问题,双webview模式去加载的时候,每次点击列表进入详情页面的时候会执行两次 id 传值,是因为加载了两次 mui.js 这个问题我还没有解决,有解决办法的可以在下面留言给我,谢谢!

demo在下面,自己下载吧!

继续阅读 »

研究了好久的首页启动白屏卡顿现象,虽然只有那么短短的几毫秒,但是看着总是不舒服,尝试了官方推荐的nativeTab_demo。

但是不想用那个小凸点的半圆球,改了好久都没有解决问题,无奈还是选用了这个,然后就在官方的代码上继续修改,终于找到突破口,略微修改了一下代码,实现了启动的时候 加载首页可以像nativeTab_demo那么迅速显示,不卡顿,而且可以少加载一个页面,直接加载的首页可以显示,不用再定义main页面。

所以也想分享给各位在坑中挣扎的兄弟们。这个是我准备给公司开发的APP用的,就直接分享基础版的东西给你们吧。代码写的不好,大神勿喷。

demo在下面,自己下载吧!

 mui.init();  

    /**此处是有四个菜单,首页不需要在这里创建,默认访问的首页会自动创建出来,所以tuan.html算第二个菜单开始**/  
    var subPages = ['tuan.html','small.html','person.html'];  
    mui.plusReady(function(){  

        //创建主体和子页面  
        var sub0 = plus.webview.getWebviewById(subPages[0]);  
        var sub1 = plus.webview.getWebviewById(subPages[1]);  
        var sub2 = plus.webview.getWebviewById(subPages[2]);  
/**此处保证页面只有第一次运行的时候去创建那几个子页面**/  
        if(!sub0 || !sub1 || !sub2){  
            var self = plus.webview.currentWebview();  
            for(var i=0;i<3;i++){  
                var sub = plus.webview.create(  
                subPages[i],  
                subPages[i],  
                {  
                    top:'0px',  
                    bottom:'51px'  
                });  
            }  
/**此处不写hide()方法也是可以的,运行时,其他页面创建出来后都在首页后面被遮住了好像,具体我也没懂**/  
        }  
        mui(".mui-bar-tab").on("tap","a",function(e){  
            var tagPage = this.getAttribute("href");  
/**此处如果是点击首页,就隐藏其他页面,就会显示首页了**/  
            if(tagPage == 'index.html'){  
                for(var j=0;j<3;j++){  
                    plus.webview.getWebviewById(subPages[j]).hide();  
                }  
            }else{  
                plus.webview.show(tagPage,"fade-in",300);  
            }  
        })  

    });

写的不好,多多指教!还有一个问题,双webview模式去加载的时候,每次点击列表进入详情页面的时候会执行两次 id 传值,是因为加载了两次 mui.js 这个问题我还没有解决,有解决办法的可以在下面留言给我,谢谢!

demo在下面,自己下载吧!

收起阅读 »

DCloud广告联盟

DCloud广告联盟为何不支持支付宝 2333

DCloud广告联盟为何不支持支付宝 2333

分享一个可左右拖拽的tab

tab

此demo有以下特点:
1、横向滚动超过一屏仍可滚动
2、子webview透明显示

代码已上传附件

此demo有以下特点:
1、横向滚动超过一屏仍可滚动
2、子webview透明显示

代码已上传附件

调用QQAPP进行聊天

qq
// str 为qq号码  
function(str){  
    if(plus.os.name == "Android") {  
        var openUrl = 'mqqwpa://im/chat?chat_type=wpa&uin=' + str;  
        plus.nativeUI.showWaiting();  
        var main = plus.android.runtimeMainActivity();  
        var Intent = plus.android.importClass('android.content.Intent');  
        var Uri = plus.android.importClass('android.net.Uri');  
        var intent = new Intent(Intent.ACTION_VIEW, Uri.parse(openUrl));  
        main.startActivity(intent);  
    }  
    if(plus.os.name == "iOS") {  
        var openUrl = 'mqq://im/chat?chat_type=wpa&uin=' + str + '&version=1&src_type=web';  
        plus.runtime.launchApplication({  
            action: openUrl  
        }, function(e) {  
            plus.nativeUI.confirm("检查到您未安装qq,请先到appstore搜索下载?", function(i) {  
                if(i.index == 0) {  
                    iosAppstore("itunes.apple.com/cn/app/mqq/");  
                }  
            });  
        });  
    }  
}
继续阅读 »
// str 为qq号码  
function(str){  
    if(plus.os.name == "Android") {  
        var openUrl = 'mqqwpa://im/chat?chat_type=wpa&uin=' + str;  
        plus.nativeUI.showWaiting();  
        var main = plus.android.runtimeMainActivity();  
        var Intent = plus.android.importClass('android.content.Intent');  
        var Uri = plus.android.importClass('android.net.Uri');  
        var intent = new Intent(Intent.ACTION_VIEW, Uri.parse(openUrl));  
        main.startActivity(intent);  
    }  
    if(plus.os.name == "iOS") {  
        var openUrl = 'mqq://im/chat?chat_type=wpa&uin=' + str + '&version=1&src_type=web';  
        plus.runtime.launchApplication({  
            action: openUrl  
        }, function(e) {  
            plus.nativeUI.confirm("检查到您未安装qq,请先到appstore搜索下载?", function(i) {  
                if(i.index == 0) {  
                    iosAppstore("itunes.apple.com/cn/app/mqq/");  
                }  
            });  
        });  
    }  
}
收起阅读 »

视频播放方面官方没有吗?

视频

强烈,希望官方视频播放方的组建,UI?

现在视频这么火,没有视频方面的APP,简直是开玩笑。。。

强烈,希望官方视频播放方的组建,UI?

现在视频这么火,没有视频方面的APP,简直是开玩笑。。。

《webkit技术内幕》读书笔记

第一章 浏览器和浏览器内核
1.渲染引擎的组成:HTML解释器、css解释器、布局(Layout)、JS引擎、绘图等

  1. Blink和Webkit的不同:
    》跨进程的iframe:为了解决iframe潜在的安全问题,为iframe创建一个单独的沙箱进程
    》将DOM引入JS引擎中,提升js访问DOM的性能
    》 接口的修改、性能优化等
    3.浏览器和对应的渲染引擎:
    Trident
    Gecko Webkit
    IE Firefox Safari Chrome Android浏览器 ChromeOS等

    第二章 HTML网页和结构
    1.webkit渲染过程
    》通过资源加载器加载URL对应的网页
    》网页被交给HTML解释器转变成Token
    》HTML解释器依据Token构建节点,形成DOM树
    》如果节点是js代码,调用js引擎解释执行
    》如果节点是图片、css、视频等资源,会调用资源加载器来加载他们,因为他们是异步的,不会阻碍当前DOM树的继续创建;如果是js资源,
    则需要停止当前DOM树的创建,直到js资源加载并被执行后才会继续DOM的创建。
    第三章 Webkit架构和模块
    1.浏览器是多进程的,包括:Browser进程、每个网页的Render进程、插件进程、GPU进程等进程:
    》Browser进程和页面渲染是分开的,这保证了页面渲染的崩溃不会导致浏览器主界面的崩溃。
    》每个页面是独立的进程,保证了页面之间相互不影响。
    》插件的进程问题不会影响浏览器主界面和网页
    》GPU加速也是独立的
    2.浏览器中的进程基本上是多线程的,如Browser进程包括UI线程和I/O线程等,网页的Render进程包括渲染线程和I/O线程等,GPU进程包括I/O线程和GL线程等:
    》 Browser进程收到用户的请求,首先交由UI线程处理,而且将相应的任务交给I/O线程,它随即将该任务传递给Render进程。
    》Render进程的I/O线程经过简单的解释后交给渲染线程,渲染线程接受请求,加载网页并渲染网页,这其中需要Browser进程获取资源和需要GPU进程来帮助渲染。最后Render进程将结构有I/O线程传递给Browser进程。
    》Browser进程接收到结果并将结果绘制出来。
    第四章 资源加载和网络栈

  2. 资源加载是一个很耗时的过程。异步执行资源(如图片、css等)的加载和执行是不会影响当前Webkit的渲染过程。同步执行的js文件会阻塞主线程的渲染过程,这会严重影响Webkit下载资源的效率;因为后面还有需要要下载的资源。这种情况下Webkit会启动另外一个线程去遍历后面的HTML网页,收集需要的资源URL,然后发送请求,这样可以避免被阻塞。与此同时,Webkit能够并发下载这些资源,甚至并发下载js代码,这种机制对于网页的加载提速是很明显的。
    2.渲染引擎、js引擎、js加载三者是互斥的?至少前两者是的。
    3.由于安全(Render进程是没有权限去获取资源的)和效率上的考虑,Render进程的资源获取实际上是通过进程间通信将任务交给Browser进程来完成,Browser进程有权限从网络和本地获取资源。
  3. Chroumium的本地缓存包括一个索引文件和四个数据文件(如果缓存比较大,那么是一个数据文件)。
  4. SDPY协议是HTTP2的基础,它的核心思想是多路复用,仅使用一个连接链传输一个网页中的众多资源:
  5. 引入新的压缩技术(一方面使用gzip或compress压缩后再发送;另一方面,客户端和服务器同时维护一张头信息表,所有字段都会存入这个表,生成一个索引号,以后就不发送同样字段了,只发送索引号)

第五章 HTML解释器和DOM模型
1.HTML解释器的流程:字节流(bytes)->字符流(characters)->词语(token)->Xss验证(可选)->节点->DOM树。 DOM树的创建只能在渲染线程上创建和访问,从字符串到词语这个阶段可以交给单独的线程来做。

  1. 当渲染引擎接收到一个事件的时候,它会通过HitTest(Webkit中一种检查触发事件在哪个区域的算法)检查哪个元素是直接的事件目标。
  2. 影子(Shadow)DOM:如<vedio>标签,其内部功能是很复杂的,但是对外只暴漏一个html标签;相当于对节点内部封装,外部看到的有限,但是内部逻辑是很复杂的。它主要解决了一个文档中可能需要大量交互的多个DOM树建立和维护各自功能边界的问题。 createShadowRoot是js中提供的创建影子DOM的方法。

第六章 CSS解释器和样式布局
1.CSS的样式规则、选择器(标签、类型、ID、属性、后代、子女、相邻同胞)、盒模型、包含块(Containing Block)模型、样式属性(背景、文本、字体、列表、表格、定位)。

  1. CSSOM:CSS对象模型。它的思想是在DOM中的一些节点接口中加入获取和操作CSS属性或者接口的js接口
  2. 布局计算是一个递归的过程,这是因为一个节点的大小通常需要计算它的子女节点的位置、大小等信息。当首次加载、viewport大小改变、动画以及通过js改变样式信息时都会触发重新计算布局。
    第七章 渲染基础
    1.Webkit的布局计算使用RenderObject树并保存计算结果到RenderObject树中,RenderObject树同其他树(如:RenderLayer树)构成Webkit渲染的主要基础设施。
  3. 为一个DOM树节点创建RenderObject对象的规则:
    》DOM书中的document节点
    》DOM树中的可视节点,如html、body、div等。Webkit不会为非可视化节点(如meta标签)创建RenderObject对象
    》某些情况下Webkit需要建立匿名RenderObject节点,该节点不对应于DOM树种的任何节点,而是为了Webkit处理的需要
    》RenderObject树节点和DOM树节点不是一一对应的关系
    3.为RenderObject节点创建新的RenderLayer节点的规则是:(不同于css3硬件加速中的复合图层)
    》document节点
    》document的子女节点,也就是html节点对应的RenderBlock节点
    》显式的指定CSS位置的RenderObject节点
    》有透明效果的RenderObject节点
    》节点有溢出(overflow)、alpha或者反射等效果的RenderObject节点
    》使用Canvas 2D或 3D(webGL)技术的RenderObject节点
    》Vedio节点对应的RenderObject节点
    4.Webkit的渲染方式主要包括三种:
    》软件渲染:绘图操作由CPU来完成
    》硬件加速渲染:绘图操作由GPU来完成
    》混合模式: 多个层的渲染结果合并到一个图像中,称之为合成渲染
    硬件渲染比较适合于3D绘图。2D绘图时GPU不一定比使用CPU有优势,主要因为:
    1.CPU的缓存机制有效的减少了重复绘制;
    2.GPU资源相对CPU的内存来说比较紧张;
    1. 软件渲染对待更新区域的处理可能优化到只需计算一个极小的区域,而硬件渲染可能需要重新绘制一层或则多层。

5.软件渲染结果基本上存储在CPU内存的一块区域,多数情况下是一个位图(Bitmap);存储结果会被copy到Browser的存储空间,然后通过Browser进程渲染出来。
第八章 硬件加速机制
1.GPU主要用来绘制3D图形,而且性能很好。GPU不能像软件渲染那样只计算其中更新的区域,需重绘所有区域;因此为了提高GPU的性能,需要对网页进行分层。分层后,部分区域的更新只在网页的一层或者几层,而不需将整个网页重绘;绘制完成后把层合成起来,即利用了GPU能力,又能减少不必要的重绘开销。
2.硬件加速机制在RenderLayer树建立后需要做三件事情来完成网页渲染:
》Webkit决定将哪些RenderLayer对象组合在一起,形成一个有后端存储的新层,这一新层不久后用户之后的合成(Compositing),这里称之为合成层(Compositing Layer)
》将每个合成层包含的这些RenderLayer内容绘制在合成层的后端存储中
》合成器将这些合成层合成起来,形成网页的最终可视化结果
3.RenderLayer对象具有以下特征之一,那么它就是合成层(参考css3硬件加速中的复合图层):
》RenderLayer 具有CSS 3D属性或者 CSS 透视效果
》RenderLayer 包含的 RenderObject 节点表示的是使用硬件加速的视频解码技术的vedio元素
》RenderLayer 包含的 RenderObject 节点表示的是使用硬件加速的Canvas 2D元素或者 WebGL 技术
》RenderLayer 使用了CSS透明效果的动画或者CSS变换的动画
》RenderLayer 使用了硬件加速的 CSS FIlter 技术
》RenderLayer 使用了剪裁(clip) 或者反射(Reflection) 属性,并且它的后代中包含一个合成层
》RenderLayer 有一个Z坐标比自己小的兄弟节点,且该节点是一个合成层
4.硬件加速最终会调用OpenGL/OpenGLES库。GPU进程最终绘制的结果不再像软件渲染那样通过共享内存传递给Browser进程,而是直接将页面的内容绘制在浏览器的标签窗口内。
5.页面加载后进行绘制时,会经历计算布局、绘图和合成三个阶段,前两者耗时较多。鉴于此提升浏览器渲染性能的两者方法:
》使用合适的网页分层技术以减少需要重新计算的布局和绘图
》使用CSS 3D 变形和动画技术:浏览器不需要重新布局,也不需要重新绘图,只需使用合成功能,而合成功能耗时非常少。
第九章 JavaScript引擎
1.解释性语言(如 js)和编译型语言(如 java c++)的区别:编译确定位置、偏移信息共享以及偏移信息查找。 三者概括起来讲是因为编译型语言可以在编译的时候确定指针位置,而解释性语言只有在执行的时候才会确定。如果找js的属性只能通过属性名匹配去查找,而c++中可以根据偏移位置去直接找到。
2.一个js引擎要包括以下几部分:
》编译器:将源代码编译成抽象语法树,某些引擎中还包括将抽象语法树转成字节码
》解释器:某些引擎中,解释器主要接受字节码,解释执行这个字节码,同时也依赖垃圾回收机制
》JIT工具:(Just-In-Time技术) 它的主要思想是当解释器将源代码解释成内部表示的时候(java字节码就是一个典型的例子),js的执行环境不仅是解释这些内部表示,而且将其中的一些字节码(主要是使用效率高的部分)转成本地代码(汇编代码),这样可以被CPU直接执行,而不是解释执行,从而极大地提高性能。
》垃圾回收器和分析工具:它们负责垃圾回收和手机引擎中的信息,帮助改善引擎的性能和功效
3.引擎的流程:
源代码 -> 抽象语法树 -> 字节码 -> 解释器 -> JIT -> 本地代码
4.v8引擎的工作原理
》数据表示: 数据和句柄,句柄指向数据存储地址。当进行垃圾回收的时候,不需要移动数据(开销大),只需修改句柄中的指针。
》V8不会把抽象语法树转成字节码或者其他中间表示,而是通过JIT直接生成本地代码。 优点:减少了抽象语法树到字节码的转换时间。 缺点:减少了中间表示(字节码)可能的优化机会、有些场景没必要生成本地代码(过度优化)
》优化回滚:编译器认为某些代码比较稳定,变量类型不会发生改变,然后生成高效的本地代码;如果引擎发现变量类型发生变化,需要使用一种机制将它做的这些错误决定回滚到之前的一般情况,这个过程就是优化回滚。优化回滚是一个很费时的操作,所以能够不回滚,肯定不要回滚。

 var count = 0;  
  function ABC(){  
      count++;  
      if(count < 1000000){  
        return 123;  
      }  
      return new Date();  
  }

》隐藏类和内嵌缓存:V8使用类和偏移位置思想,将本来需要通过字符串匹配来查找属性值的算法改进为使用类似C++编译器的偏移位置机制来实现,这就是隐藏类

  function ABC(x, y){  
    this.x = x;  
    this.y = y;  
  }  
  var a = new ABC(1,1);  
  var b = new ABC(1,2);

上述例子中的a、b两个对象包含相同的属性名,因此它们被归为同一个组,也就是隐藏类,这些属性在隐藏类中有相同的偏移值。当访问这些对象的属性时,可以通过偏移值知道它们的位置并进行访问。
内嵌缓存的基本思想是将使用之前查找的结果缓存起来,也就是说V8可以将之前查找的隐藏类和偏移值保存下来。当下次查找的时候,首先比较当前对象是否也是之前的隐藏类,如果是的话,可以直接使用之前的偏移值,从而减少查找表的时间。
》内存管理:V8采用的分代垃圾回收机制,分为年轻分代、年老分代等。
》快照机制:快照机制就是将这些内置的对象和函数(如 Math、String、Array)加载之后的内存保存并被序列化。序列化之后的结果很容易被反序列化,经过快照机制的启动时间,可以缩减几毫秒。
5.JavaScriptCore和V8不同,它会生成平台无关的字节码(和java类似),然后基于此做优化,同时它的句柄不论在32位还是64位平台上,都使用64位来表示。

  1. 编写高效的JavaScript代码:
    》类型:把相同类型的元素放到一个数组中,这样引擎可以通过偏移位置来访问他们
    》数据表示:简单类型的数据可以直接保存在句柄中,这样可以有效地减少寻址时间和内存的使用。对于数值来说,能使用证书的尽量不要使用浮点数。
    》内存:及时触发垃圾回收,可以把不再使用的对象的变量设置为空:a.x=null,或者通过delete关键字进行删除:delete a.x ;但是后者有可能会由于使用了隐藏类,需要创建新的隐藏类,进而带来一些复杂的额外操作。
    》优化回滚:不要写触发优化回滚的代码
    》新机制:如通过requestAnimationFrame替代setInterval实现动画等。

作者:狐尼克朱迪
链接:https://www.jianshu.com/p/506447128ed4
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
css3硬件加速
https://www.jianshu.com/p/f8b1d6e598db

继续阅读 »

第一章 浏览器和浏览器内核
1.渲染引擎的组成:HTML解释器、css解释器、布局(Layout)、JS引擎、绘图等

  1. Blink和Webkit的不同:
    》跨进程的iframe:为了解决iframe潜在的安全问题,为iframe创建一个单独的沙箱进程
    》将DOM引入JS引擎中,提升js访问DOM的性能
    》 接口的修改、性能优化等
    3.浏览器和对应的渲染引擎:
    Trident
    Gecko Webkit
    IE Firefox Safari Chrome Android浏览器 ChromeOS等

    第二章 HTML网页和结构
    1.webkit渲染过程
    》通过资源加载器加载URL对应的网页
    》网页被交给HTML解释器转变成Token
    》HTML解释器依据Token构建节点,形成DOM树
    》如果节点是js代码,调用js引擎解释执行
    》如果节点是图片、css、视频等资源,会调用资源加载器来加载他们,因为他们是异步的,不会阻碍当前DOM树的继续创建;如果是js资源,
    则需要停止当前DOM树的创建,直到js资源加载并被执行后才会继续DOM的创建。
    第三章 Webkit架构和模块
    1.浏览器是多进程的,包括:Browser进程、每个网页的Render进程、插件进程、GPU进程等进程:
    》Browser进程和页面渲染是分开的,这保证了页面渲染的崩溃不会导致浏览器主界面的崩溃。
    》每个页面是独立的进程,保证了页面之间相互不影响。
    》插件的进程问题不会影响浏览器主界面和网页
    》GPU加速也是独立的
    2.浏览器中的进程基本上是多线程的,如Browser进程包括UI线程和I/O线程等,网页的Render进程包括渲染线程和I/O线程等,GPU进程包括I/O线程和GL线程等:
    》 Browser进程收到用户的请求,首先交由UI线程处理,而且将相应的任务交给I/O线程,它随即将该任务传递给Render进程。
    》Render进程的I/O线程经过简单的解释后交给渲染线程,渲染线程接受请求,加载网页并渲染网页,这其中需要Browser进程获取资源和需要GPU进程来帮助渲染。最后Render进程将结构有I/O线程传递给Browser进程。
    》Browser进程接收到结果并将结果绘制出来。
    第四章 资源加载和网络栈

  2. 资源加载是一个很耗时的过程。异步执行资源(如图片、css等)的加载和执行是不会影响当前Webkit的渲染过程。同步执行的js文件会阻塞主线程的渲染过程,这会严重影响Webkit下载资源的效率;因为后面还有需要要下载的资源。这种情况下Webkit会启动另外一个线程去遍历后面的HTML网页,收集需要的资源URL,然后发送请求,这样可以避免被阻塞。与此同时,Webkit能够并发下载这些资源,甚至并发下载js代码,这种机制对于网页的加载提速是很明显的。
    2.渲染引擎、js引擎、js加载三者是互斥的?至少前两者是的。
    3.由于安全(Render进程是没有权限去获取资源的)和效率上的考虑,Render进程的资源获取实际上是通过进程间通信将任务交给Browser进程来完成,Browser进程有权限从网络和本地获取资源。
  3. Chroumium的本地缓存包括一个索引文件和四个数据文件(如果缓存比较大,那么是一个数据文件)。
  4. SDPY协议是HTTP2的基础,它的核心思想是多路复用,仅使用一个连接链传输一个网页中的众多资源:
  5. 引入新的压缩技术(一方面使用gzip或compress压缩后再发送;另一方面,客户端和服务器同时维护一张头信息表,所有字段都会存入这个表,生成一个索引号,以后就不发送同样字段了,只发送索引号)

第五章 HTML解释器和DOM模型
1.HTML解释器的流程:字节流(bytes)->字符流(characters)->词语(token)->Xss验证(可选)->节点->DOM树。 DOM树的创建只能在渲染线程上创建和访问,从字符串到词语这个阶段可以交给单独的线程来做。

  1. 当渲染引擎接收到一个事件的时候,它会通过HitTest(Webkit中一种检查触发事件在哪个区域的算法)检查哪个元素是直接的事件目标。
  2. 影子(Shadow)DOM:如<vedio>标签,其内部功能是很复杂的,但是对外只暴漏一个html标签;相当于对节点内部封装,外部看到的有限,但是内部逻辑是很复杂的。它主要解决了一个文档中可能需要大量交互的多个DOM树建立和维护各自功能边界的问题。 createShadowRoot是js中提供的创建影子DOM的方法。

第六章 CSS解释器和样式布局
1.CSS的样式规则、选择器(标签、类型、ID、属性、后代、子女、相邻同胞)、盒模型、包含块(Containing Block)模型、样式属性(背景、文本、字体、列表、表格、定位)。

  1. CSSOM:CSS对象模型。它的思想是在DOM中的一些节点接口中加入获取和操作CSS属性或者接口的js接口
  2. 布局计算是一个递归的过程,这是因为一个节点的大小通常需要计算它的子女节点的位置、大小等信息。当首次加载、viewport大小改变、动画以及通过js改变样式信息时都会触发重新计算布局。
    第七章 渲染基础
    1.Webkit的布局计算使用RenderObject树并保存计算结果到RenderObject树中,RenderObject树同其他树(如:RenderLayer树)构成Webkit渲染的主要基础设施。
  3. 为一个DOM树节点创建RenderObject对象的规则:
    》DOM书中的document节点
    》DOM树中的可视节点,如html、body、div等。Webkit不会为非可视化节点(如meta标签)创建RenderObject对象
    》某些情况下Webkit需要建立匿名RenderObject节点,该节点不对应于DOM树种的任何节点,而是为了Webkit处理的需要
    》RenderObject树节点和DOM树节点不是一一对应的关系
    3.为RenderObject节点创建新的RenderLayer节点的规则是:(不同于css3硬件加速中的复合图层)
    》document节点
    》document的子女节点,也就是html节点对应的RenderBlock节点
    》显式的指定CSS位置的RenderObject节点
    》有透明效果的RenderObject节点
    》节点有溢出(overflow)、alpha或者反射等效果的RenderObject节点
    》使用Canvas 2D或 3D(webGL)技术的RenderObject节点
    》Vedio节点对应的RenderObject节点
    4.Webkit的渲染方式主要包括三种:
    》软件渲染:绘图操作由CPU来完成
    》硬件加速渲染:绘图操作由GPU来完成
    》混合模式: 多个层的渲染结果合并到一个图像中,称之为合成渲染
    硬件渲染比较适合于3D绘图。2D绘图时GPU不一定比使用CPU有优势,主要因为:
    1.CPU的缓存机制有效的减少了重复绘制;
    2.GPU资源相对CPU的内存来说比较紧张;
    1. 软件渲染对待更新区域的处理可能优化到只需计算一个极小的区域,而硬件渲染可能需要重新绘制一层或则多层。

5.软件渲染结果基本上存储在CPU内存的一块区域,多数情况下是一个位图(Bitmap);存储结果会被copy到Browser的存储空间,然后通过Browser进程渲染出来。
第八章 硬件加速机制
1.GPU主要用来绘制3D图形,而且性能很好。GPU不能像软件渲染那样只计算其中更新的区域,需重绘所有区域;因此为了提高GPU的性能,需要对网页进行分层。分层后,部分区域的更新只在网页的一层或者几层,而不需将整个网页重绘;绘制完成后把层合成起来,即利用了GPU能力,又能减少不必要的重绘开销。
2.硬件加速机制在RenderLayer树建立后需要做三件事情来完成网页渲染:
》Webkit决定将哪些RenderLayer对象组合在一起,形成一个有后端存储的新层,这一新层不久后用户之后的合成(Compositing),这里称之为合成层(Compositing Layer)
》将每个合成层包含的这些RenderLayer内容绘制在合成层的后端存储中
》合成器将这些合成层合成起来,形成网页的最终可视化结果
3.RenderLayer对象具有以下特征之一,那么它就是合成层(参考css3硬件加速中的复合图层):
》RenderLayer 具有CSS 3D属性或者 CSS 透视效果
》RenderLayer 包含的 RenderObject 节点表示的是使用硬件加速的视频解码技术的vedio元素
》RenderLayer 包含的 RenderObject 节点表示的是使用硬件加速的Canvas 2D元素或者 WebGL 技术
》RenderLayer 使用了CSS透明效果的动画或者CSS变换的动画
》RenderLayer 使用了硬件加速的 CSS FIlter 技术
》RenderLayer 使用了剪裁(clip) 或者反射(Reflection) 属性,并且它的后代中包含一个合成层
》RenderLayer 有一个Z坐标比自己小的兄弟节点,且该节点是一个合成层
4.硬件加速最终会调用OpenGL/OpenGLES库。GPU进程最终绘制的结果不再像软件渲染那样通过共享内存传递给Browser进程,而是直接将页面的内容绘制在浏览器的标签窗口内。
5.页面加载后进行绘制时,会经历计算布局、绘图和合成三个阶段,前两者耗时较多。鉴于此提升浏览器渲染性能的两者方法:
》使用合适的网页分层技术以减少需要重新计算的布局和绘图
》使用CSS 3D 变形和动画技术:浏览器不需要重新布局,也不需要重新绘图,只需使用合成功能,而合成功能耗时非常少。
第九章 JavaScript引擎
1.解释性语言(如 js)和编译型语言(如 java c++)的区别:编译确定位置、偏移信息共享以及偏移信息查找。 三者概括起来讲是因为编译型语言可以在编译的时候确定指针位置,而解释性语言只有在执行的时候才会确定。如果找js的属性只能通过属性名匹配去查找,而c++中可以根据偏移位置去直接找到。
2.一个js引擎要包括以下几部分:
》编译器:将源代码编译成抽象语法树,某些引擎中还包括将抽象语法树转成字节码
》解释器:某些引擎中,解释器主要接受字节码,解释执行这个字节码,同时也依赖垃圾回收机制
》JIT工具:(Just-In-Time技术) 它的主要思想是当解释器将源代码解释成内部表示的时候(java字节码就是一个典型的例子),js的执行环境不仅是解释这些内部表示,而且将其中的一些字节码(主要是使用效率高的部分)转成本地代码(汇编代码),这样可以被CPU直接执行,而不是解释执行,从而极大地提高性能。
》垃圾回收器和分析工具:它们负责垃圾回收和手机引擎中的信息,帮助改善引擎的性能和功效
3.引擎的流程:
源代码 -> 抽象语法树 -> 字节码 -> 解释器 -> JIT -> 本地代码
4.v8引擎的工作原理
》数据表示: 数据和句柄,句柄指向数据存储地址。当进行垃圾回收的时候,不需要移动数据(开销大),只需修改句柄中的指针。
》V8不会把抽象语法树转成字节码或者其他中间表示,而是通过JIT直接生成本地代码。 优点:减少了抽象语法树到字节码的转换时间。 缺点:减少了中间表示(字节码)可能的优化机会、有些场景没必要生成本地代码(过度优化)
》优化回滚:编译器认为某些代码比较稳定,变量类型不会发生改变,然后生成高效的本地代码;如果引擎发现变量类型发生变化,需要使用一种机制将它做的这些错误决定回滚到之前的一般情况,这个过程就是优化回滚。优化回滚是一个很费时的操作,所以能够不回滚,肯定不要回滚。

 var count = 0;  
  function ABC(){  
      count++;  
      if(count < 1000000){  
        return 123;  
      }  
      return new Date();  
  }

》隐藏类和内嵌缓存:V8使用类和偏移位置思想,将本来需要通过字符串匹配来查找属性值的算法改进为使用类似C++编译器的偏移位置机制来实现,这就是隐藏类

  function ABC(x, y){  
    this.x = x;  
    this.y = y;  
  }  
  var a = new ABC(1,1);  
  var b = new ABC(1,2);

上述例子中的a、b两个对象包含相同的属性名,因此它们被归为同一个组,也就是隐藏类,这些属性在隐藏类中有相同的偏移值。当访问这些对象的属性时,可以通过偏移值知道它们的位置并进行访问。
内嵌缓存的基本思想是将使用之前查找的结果缓存起来,也就是说V8可以将之前查找的隐藏类和偏移值保存下来。当下次查找的时候,首先比较当前对象是否也是之前的隐藏类,如果是的话,可以直接使用之前的偏移值,从而减少查找表的时间。
》内存管理:V8采用的分代垃圾回收机制,分为年轻分代、年老分代等。
》快照机制:快照机制就是将这些内置的对象和函数(如 Math、String、Array)加载之后的内存保存并被序列化。序列化之后的结果很容易被反序列化,经过快照机制的启动时间,可以缩减几毫秒。
5.JavaScriptCore和V8不同,它会生成平台无关的字节码(和java类似),然后基于此做优化,同时它的句柄不论在32位还是64位平台上,都使用64位来表示。

  1. 编写高效的JavaScript代码:
    》类型:把相同类型的元素放到一个数组中,这样引擎可以通过偏移位置来访问他们
    》数据表示:简单类型的数据可以直接保存在句柄中,这样可以有效地减少寻址时间和内存的使用。对于数值来说,能使用证书的尽量不要使用浮点数。
    》内存:及时触发垃圾回收,可以把不再使用的对象的变量设置为空:a.x=null,或者通过delete关键字进行删除:delete a.x ;但是后者有可能会由于使用了隐藏类,需要创建新的隐藏类,进而带来一些复杂的额外操作。
    》优化回滚:不要写触发优化回滚的代码
    》新机制:如通过requestAnimationFrame替代setInterval实现动画等。

作者:狐尼克朱迪
链接:https://www.jianshu.com/p/506447128ed4
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
css3硬件加速
https://www.jianshu.com/p/f8b1d6e598db

收起阅读 »

jquery $.extend()用法总结

jquery

jquery为开发插件提供了两个方法,分别是:

jQuery.fn.extend(object);  
jQuery.extend(object);

jQuery.fn.extend(object);给jQuery对象添加方法。
jQuery.extend(object);为扩展jQuery类本身.为类添加新的方法。
注意用法上的区别
jQuery.fn.myPlugin的用法为 $('.ye').myPlugin
jQuery.extend的用法为 $.add(3,5);

jQuery.fn.myPlugin = function(options) {  
                $options = $.extend({  
                    html: "no messages",  
                    css : {  
                        "color":"red",  
                        "font-size": "14px"  
                    }},  
                    options);  
                    return $(this).css({  
                          "color": $options.css.color  
                    }).html($options.html);  
            }  
            $('.ye').myPlugin({  
                html: "so esay,yes?",  
                css: {"color":"green","font-size":"20px"}  
            });

1.合并多个对象
这是使用的就是$.extend( )的嵌套多个对象的功能。
所谓嵌套多个对象,有点类似于数组的合并的操作。但是这里是对象。举例说明:
用法 jQuery.extend(obj1,obj2,obj3...)

var css1 = {size:"10px",style: "oblique"};  
var css2 = {size:"12px",style:"oplique",weight:"bolder"};  
jQuery.extend(css1,css2);  
//结果:css1的size属性被覆盖,而且继承了css2的weight属性  
//css1 = {size: "12px",style: “oblique” ,weight: "bolder"}

2.深度嵌套对象

jQuery.extend(  
{name: "John",location: {city: "Boston"}},  
{last: "Resig",location:{state: "MA"}}  
);  
//结果  
// => {name:"John",last: "Resig",location:{state:"MA"}}   
//单层合并,city值丢失,后边的location覆盖了前边的location  

//更深入的 .extend()  
jQuery.extend(true,  
{name: "John",location: {city: "Boston"}},  
 {last: "Resig",location:{state: "MA"}}  
);  
//结果  
// => {name:"John",last: "Resig",location:{city:"Boston",state:"MA"}}  
//city与state合并为新的对象

3.可以给jQuery添加静态方法

$.extend({  
        add: function (a,b){  
            return a + b;  
        },  
        minus: function(a,b){  
            return a - b;  
        },  
        multiply: function(a,b){  
            return a*b;  
        },  
        divide:function(a,b){  
            return Math.floor(a/b);  
        }  
});  
var sum = $.add(3,5) + $.minus(3,5) + $.multiply(3,5)+ $.divide($options,7);  
console.log(sum);
继续阅读 »

jquery为开发插件提供了两个方法,分别是:

jQuery.fn.extend(object);  
jQuery.extend(object);

jQuery.fn.extend(object);给jQuery对象添加方法。
jQuery.extend(object);为扩展jQuery类本身.为类添加新的方法。
注意用法上的区别
jQuery.fn.myPlugin的用法为 $('.ye').myPlugin
jQuery.extend的用法为 $.add(3,5);

jQuery.fn.myPlugin = function(options) {  
                $options = $.extend({  
                    html: "no messages",  
                    css : {  
                        "color":"red",  
                        "font-size": "14px"  
                    }},  
                    options);  
                    return $(this).css({  
                          "color": $options.css.color  
                    }).html($options.html);  
            }  
            $('.ye').myPlugin({  
                html: "so esay,yes?",  
                css: {"color":"green","font-size":"20px"}  
            });

1.合并多个对象
这是使用的就是$.extend( )的嵌套多个对象的功能。
所谓嵌套多个对象,有点类似于数组的合并的操作。但是这里是对象。举例说明:
用法 jQuery.extend(obj1,obj2,obj3...)

var css1 = {size:"10px",style: "oblique"};  
var css2 = {size:"12px",style:"oplique",weight:"bolder"};  
jQuery.extend(css1,css2);  
//结果:css1的size属性被覆盖,而且继承了css2的weight属性  
//css1 = {size: "12px",style: “oblique” ,weight: "bolder"}

2.深度嵌套对象

jQuery.extend(  
{name: "John",location: {city: "Boston"}},  
{last: "Resig",location:{state: "MA"}}  
);  
//结果  
// => {name:"John",last: "Resig",location:{state:"MA"}}   
//单层合并,city值丢失,后边的location覆盖了前边的location  

//更深入的 .extend()  
jQuery.extend(true,  
{name: "John",location: {city: "Boston"}},  
 {last: "Resig",location:{state: "MA"}}  
);  
//结果  
// => {name:"John",last: "Resig",location:{city:"Boston",state:"MA"}}  
//city与state合并为新的对象

3.可以给jQuery添加静态方法

$.extend({  
        add: function (a,b){  
            return a + b;  
        },  
        minus: function(a,b){  
            return a - b;  
        },  
        multiply: function(a,b){  
            return a*b;  
        },  
        divide:function(a,b){  
            return Math.floor(a/b);  
        }  
});  
var sum = $.add(3,5) + $.minus(3,5) + $.multiply(3,5)+ $.divide($options,7);  
console.log(sum);
收起阅读 »