>  기사  >  웹 프론트엔드  >  JS 개발 관련 질문에 대한 가장 실용적인 답변

JS 개발 관련 질문에 대한 가장 실용적인 답변

亚连
亚连원래의
2018-05-17 11:26:571293검색

Maintainability

대부분의 개발자는 다른 사람의 코드를 유지 관리하는 데 많은 시간을 소비하기 때문에 유지 관리 가능한 코드를 작성하는 것이 중요합니다. 새로운 코드를 처음부터 개발하는 것은 어려우며, 대부분의 경우 다른 사람의 작업을 기반으로 합니다. 다른 개발자가 이를 바탕으로 더 나은 작업을 수행할 수 있도록 자신의 코드에 대한 유지 관리 가능성을 보장하세요.

유지보수 가능한 코드란 무엇인가요?

이해성: 원래 개발자의 완전한 설명 없이도 다른 사람이 코드를 넘겨받아 코드의 의도와 일반적인 접근 방식을 이해할 수 있습니다.

직관성: 코드의 내용이 어떻게 작동하든 한눈에 이해할 수 있습니다.

적응성: 데이터 변경 시 완전히 다시 작성할 필요가 없는 방식으로 코드가 작성되었습니다.

확장성: 코드는 핵심 기능의 향후 확장을 허용하도록 설계되었습니다.

디버깅 가능성: 문제가 발생하면 코드에서 다음을 수행할 수 있습니다. 가능한 한 직접적으로 문제를 판단할 수 있는 충분한 정보

코딩 규칙

코드 규칙은 JavaScript 코드에 대한 작성 규칙 집합을 구성하여 코드를 유지 관리할 수 있게 만드는 간단한 방법입니다.

가독성: 코드를 유지 관리하려면 먼저 읽을 수 있어야 합니다. 가독성의 대부분은 코드 들여쓰기와 관련이 있으며 또 다른 측면은 주석입니다. 다음 측면을 포함하세요.

함수 및 메서드: 각 함수 또는 메서드에는 작업을 완료하는 데 사용할 수 있는 알고리즘과 목적을 설명하는 설명이 포함되어야 합니다. 매개변수가 무엇을 나타내는지, 함수에 반환 값이 있는지 여부 등 가정을 미리 명시하는 것도 매우 중요합니다.

긴 코드: 특정 작업을 완료하는 데 사용되는 여러 줄의 코드 앞에는 작업을 설명하는 주석이 와야 합니다.

복잡한 알고리즘: 문제를 해결하기 위해 독특한 방법을 사용했다면 댓글에 어떻게 사용했는지 설명해주세요. 이는 다른 사람들이 귀하의 코드를 보는 데 도움이 될 뿐만 아니라 다음에 귀하가 직접 코드를 볼 때 이를 이해하는 데도 도움이 될 것입니다.

해킹: 브라우저 차이로 인해 js 코드에는 일반적으로 일부 핵이 포함되어 있습니다. 특정 브라우저가 일반적인 방법을 지원하지 않아 다른 것을 사용해야 한다면 댓글로 남겨주세요.

변수 및 함수 이름

이름 지정 규칙은 다음과 같습니다.

변수 이름은 자동차 또는 사람과 같은 명사여야 합니다.

함수 이름은 getName()과 같은 동사로 시작해야 합니다. isEnable()

과 같이 is로 시작하는 부울 값을 반환하는 함수는 변수 이름과 함수가 논리적 이름을 사용해야 하므로 길이에 대해 걱정하지 마세요. 후처리 및 압축을 통해 길이를 줄일 수 있습니다.

변수 유형 투명성

변수의 데이터 유형 을 나타내는 세 가지 방법이 있습니다.

초기화: 변수가 정의되면 변수를 다음 값으로 초기화해야 합니다. 미래를 암시합니다. 적용 방법.

예: varfound = false;
특정 데이터 유형으로 초기화하면 변수 유형을 잘 나타낼 수 있습니다. 그러나 단점은 함수 선언의 매개변수에 사용할 수 없다는 것입니다.

헝가리어 표기법을 사용하여 변수 유형을 지정합니다.

"o"는 객체를 나타내고, "s"는 문자열을 나타내고, "i"는 정수를 나타내고, "f"는 부동 소수점 숫자를 나타내고, "b"는 부울을 나타냅니다.
예: var bFound;/ /Boolean
헝가리어 표기법의 장점은
함수 매개변수도 사용할 수 있다는 것입니다. 그러나 단점은 코드를 읽기 어렵게 만들어 코드가 없으면 코드의 직관적이고 문장 같은 품질을 방해한다는 것입니다.

유형 주석 사용

var found/*:Booldan*/=false;1

유형 주석의 단점은 여러 줄 주석을 사용하여 한 번에 큰 코드 블록에 주석을 달 수 없다는 것입니다. 유형 주석도 여러 줄 주석이고 둘이 충돌하기 때문입니다.

松散耦合

只要应用的某个部分过分依赖于另一部分,代码就是耦合过紧、难于维护。典型的问题如:对象直接引用另一个对象,并且当修改其中一个的同时需要修改另外一个。紧密耦合的软件难于维护并且需要经常重写。

解耦html/javascript

html和javascript各自代表了解决方案中的不同层次:html是数据,javascript是行为。因为他们天生就需要交互,所以有多种不同的方法将这两个技术关联起来。

情况一:直接写在html中的javascript,使用包含内联代码的3f1c4e4b6b16bbbd69b2ee476dc4f83a元素或者使用Html属性来分配事件处理程序,都是过于紧密的耦合。 
比如: 
从技术上说都是正确的,但是在实践中,它们将表示数据的html和定义行为的JavaScript紧密耦合在了一起。**理想情况是:**html和JavaScript应该完全分离,并通过外部文件和使用DOM附件行为来包含JavaScript。 
当两者耦合在一起时,出现javascript错误时就要先判断错误出现在Html部分还是javascript文件中。还会引入和代码是否可用的相关问题。 
情况二: 
html和javascript的机密耦合也可以在相反的关系上成立:javascript包含了Html.这通常会出现在使用innerHtml来插入一段Html。 
将html和JavaScript解耦可以在调试过程中节省实践,更加容易确定错误的来源,也减轻维护的难度,更改行为只需要在JavaScript文件中进行,而更改标记则只要在渲染文件中。

解耦css/javascript

另外一个web层则是css,它主要负责页面的显示。 
最常见的紧密耦合例子是使用javascript来更改某些样式,如下所示:

//css对javascript的紧密耦合  
element.style.color = "red";  element.style.backgroundColor = "blue";  123

由于CSS负责页面的显示,当显示出现问题时应该只是查看CSS文件来解决。然而,当使用了JavaScript来更改某些样式的时候,如上面代码所示,就出现了第二个可能已更改的和必须检查的地方。结果是JavaScript也在某种程度上负责了页面的显示,并与CSS紧密耦合了。如果将来需要修改样式,CSS和JavaScript文件可能都需要修改,这会给维护人员带来噩梦。 
解耦合的原则: 
虽然不可能完全将css和javascript解耦,但是还是能让耦合更松散的,这是通过动态改变样式类而非特定样式来实现的,如下所示:

//css对javascript的松散耦合   element.className = "edit";  12

通过只修改某个元素的css类,就可以让大部分样式信息严格保留在css中。JavaScript可以改变样式类,但并不会直接影响到元素的样式,只要使用了正确的类,那么任何显示问题都可以直接追溯到css而非JavaScript。

第二类紧密耦合仅会在IE中出现(标准模式下的IE8不会出现),它可以在css中通过表达式嵌入javascript,例如:

/*javascript对css的紧密耦合*/   div{   
width:expression(document.body.offsetwidth-10+"px");  
}1234

通常要避免使用表达式,因为他们不能跨浏览器兼容,还因为它们所引入的JavaScript和css之间的紧密耦合。 
好的层次划分是非常重要的。显示问题的唯一来源应该是css,行为问题的唯一来源应该是javascipt.在这些层次之间保持松散耦合可以让你的整个应用更加易于维护。

解耦应用逻辑/事件处理程序

很少有能仔细得将应用逻辑从事件处理程序中分离的。例如:

fuction handleKeyPress(event){  
if(event.keyCode == 13){  
 var target = eventUtil.getTarget(event);  
 //应用逻辑部分
 var value = 5 * parseInt(target.value);  
 if(value >10){    
 document.getElementById("error-msg").style.display = "block";   
            }  
        }  
}12345678910

这段代码除了包含应用逻辑,还进行了事件的处理。 
这种方式的问题有其双重性:首先,除了通过事件之外就再没有方法执行应用逻辑,让调试变得困难。如果没有发生预想的结果怎么办?是不是表示事件处理程序没有被调用还会死指应用逻辑失败?其次,如果一个后续的事件引发同样的应用逻辑,那就必须复制功能代码或将代码抽取到一个单独的函数中。无论如何,都要做比实际所需更多的改动。

较好的方法是将应用逻辑和时间处理程序分离,这两者分别处理各自的东西。一个事件处理程序应该从事件对象中提取相关信息,并将这些信息传送到处理应用逻辑的某个方法中。例如前面的代码重写为:

//处理应用逻辑function validateValue(value){  
var value = 5 * parseInt(value);  
 if(value >10){  
 document.getElementById("error-msg").style.display = "block";   
}  
} 
//事件处理函数  fuction handleKeyPress(event){  
 if(event.keyCode==13){  
var target = EventUtil.getTarget(event);  
validateValue(target.value);  
}  
} 1234567891011121314

好处:从事件处理程序中分离应用逻辑有几个好处。首先,可以让你更容易更改触发特定过程的事件。如果最开始由鼠标点击事件触发过程,但现在按键也要进行同样处理,这种更改就很容易。其次,可以在不附加到事件的情况下测试代码,使其更容易创建单元测试或者是自动化应用流程。 
以下是要牢记的应用和业务逻辑之间松散耦合的几条原则:

勿将event对象传递给其他方法;只传来自event对象中所需的数据

任何可以在应用层面的动作都应该可以在不执行任何事件处理程序的情况下进行;

任何事件处理程序都应该处理事件,然后将处理转交给应用逻辑。

牢记着几条可以在任何代码中都获得极大的可维护性的改进,并且为进一步的测试和开发制造了很多可能。

js性能

优化DOM交互

在js中,DOM毫无疑问是最慢的一部分。DOM操作与交互要消耗大量时间,因为它们往往需要重新渲染整个页面或者一部分。理解如何优化与DOM的交互可以极大提高脚本完成的速度。

最小化现场更新

一旦你需要访问的DOM部分是已经显示的页面的一部分,那么你就是在进行一个现场更新,因为需要立即对页面对用户的显示进行更新。 
每一个更改,不管是插入单个字符,还是移除整个片段,都有一个性能惩罚,因为浏览器要重新计算无数尺寸以进行更新。现场更新进行得越多,代码完成执行所花的时间就越长,完成一个操作所需的现场更新越少,代码就越快。 
demo1:

<body><ul id="list"></ul><script type="text/javascript">var oList = document.getElementById("list");
console.time("计时器1");for(var i=0;i<1000;i++){    var item = document.createElement("li");
    list.appendChild(item);
    item.appendChild(document.createTextNode("item"+i));
}
console.timeEnd("计时器1");</script></body>12345678910111213

上面这段代码为列表添加了1000个项目,添加每个项目时,都需要2个现场更新:一个添加25edfb22a4f469ecb59f1190150159c6元素,另一个给它添加文本节点。这样添加1000个项目,这个操作总共要完成2000个现场更新。 
修正这个瓶颈(需要减少现场更新的数量)有两种方法: 
1.将列表从页面上移除,最后进行更新,最后再讲列表插回到同样的位置。这个方法的问题在于:每次页面更新的时候都会出现不必要的闪烁。 
2.使用文档碎片来构建DOM结构,接着将其添加到List元素中。这种方式避免了现场更新和页面闪烁的问题。(请看Demo2) 
Demo2:

<body><ul id="list"></ul><script type="text/javascript">var oList = document.getElementById("list");var fragment = document.createDocumentFragment();
console.time("计时器2");for(var i=0;i<1000;i++){    var item = document.createElement("li");
    fragment.appendChild(item);
    item.appendChild(document.createTextNode("item"+i));
}//记住,当给appendChild()传入文档碎片时,只有碎片中的子节点被添加到目标,碎片本身不会被添加的list.appendChild(fragment);
console.timeEnd("计时器2");</script></body>12345678910111213141516

在这个例子中只有一次现场更新,它发生在所有项目都创建好之后。文档碎片用作一个临时的占位符,放置新创建的项目,然后使用appendChild()将所有项目添加到列表中。 
一旦需要更新DOM,考虑使用文档碎片来构建DOM结构,然后再将其添加到现在的文档中。 
两个Demo的执行时间比较: 

使用innerHTML

在页面上创建DOM节点的方法: 
1.使用诸如createElement()和appendChild()之类的DOM方法 
2.使用innerHTML 
对于小的DOM更改而言,两种方法效率都差不多,然而,对于大的DOM更改,使用innerHTML要比使用标准DOM方法创建同样的DOM结构快得多。 
原因:当把innerHTML设置为某个值时,后台会创建一个HTML解析器,然后使用内部的DOM调用来创建DOM结构,而非基于JavaScript的DOM调用。由于内部方法是编译好的而非解释执行的,所以执行快得多。 
Demo:

<body><ul id="list"></ul><script type="text/javascript">var oList = document.getElementById("list"),
    html = "",
    i;for(i=0;i<2000;i++){
    html += "<li>item " + i +"</li>";
}
oList.innerHTML = html;//这里构建好一个字符串然后一次性调用innerHTML,这样也只发生一次现场更新</script></body>12345678910111213

使用事件代理

页面上的事件处理程序的数量和页面响应用户交互的速度之间是负相关。为了减少这种惩罚,最好使用事件代理。 
事件代理:用到了事件冒泡。任何可以冒泡的事件都不仅仅可以在事件目标上进行处理,目标的任何祖先节点上也能处理。利用这一点,就可以将事件处理程序附加到更高层的地方负责多个目标的事件处理。如果在文档级别附加事件处理程序,这样可以处理整个页面的事件。 
Demo:

<!DOCTYPE html><html><head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>事件委托</title></head><body><ul id="list1">
    <li><a href="#">正在监听1</a></li>
    <li><a href="#">正在监听1</a></li>
    <li><a href="#">正在监听1</a></li>
    <li><a href="#">正在监听1</a></li></ul><ul id="list2">
    <li><a href="#">正在监听2</a></li>
    <li><a href="#">正在监听2</a></li>
    <li><a href="#">正在监听2</a></li>
    <li><a href="#">正在监听2</a></li></ul><a href="#" id="add-items">添加更多节点</a><script type="text/javascript">var oList1 = document.querySelector("#list1");var aList = oList1.querySelectorAll("li");var oList2 = document.querySelector("#list2");var oBtn = document.querySelector("#add-items");for(var i=0,len=aList.length;i<len;i++){
    aList[i].onclick = function(){
        alert("正在监听1");
    }
}
oList2.onclick = function(){
    alert("正在监听2");
}    var html = "<li><a href=&#39;#&#39;&#39;>我是动态新增节点</a></li>";
oBtn.onclick = function(){
    oList1.innerHTML += html;
    oList2.innerHTML += html;
}</script></body></html>123456789101112131415161718192021222324252627282930313233343536373839404142

事件委托好处: 
1.能够有效减少页面上事件处理程序的数量 
2.对于动态增加的子节点,依旧能够实现事件的绑定

最小化访问HTMLCollection的次数

最小化访问HTMLCollection的次数可以极大地改进脚本的性能

<body><script type="text/javascript">//这里关键在于长度length存入了len变量,而不是每次都去访问HTMLCollection的length属性var images = document.
getElementsByTagName
("img"),
    image,
    i,len;for(i=0,len=images.length;i<len;i++){
    image = images[i];    //其他处理}</script></body>123456789101112

注意作用域

避免全局查找

可能优化脚本性能最重要的就是注意全局查找。使用全局变量和函数肯定要比局部的开销更大,因为要涉及作用域链上的查找。 
下面的代码包含了3个对于全局document对象的引用。如果在页面上有多个图片,那么for循环中的document引用就会被执行多次甚至上百次,每次都会进行作用域链查找。

<script type="text/javascript">/*
通过创建一个指向document对象的局部变量,就可以通过限制一次全局查找来提升这个函数的性能。
 */function updateUI(){
    var imgs = document.getElementsByTagName("img");    for(var i=0,len=imgs.length;i<len;i++){
        imgs[i].title = document.title + "image" +i;
    }    var msg = document.getElementById("msg");
    msg.innerHTML = "Update complete";
}function updateUI(){
    //缓存全局变量,减少全局变量的查找。这样就减少了话费在作用域上查找的时间,就能提升脚本的性能。改进后的函数只有一次全局查找。
    var doc = document;    var imgs = doc.getElementsByTagName("img");    for(var i=0,len=imgs.length;i<len;i++){
        imgs[i].title = doc.title + "image" +i;
    }    var msg = doc.getElementById("msg");
    msg.innerHTML = "Update complete";
}</script>123456789101112131415161718192021222324

下面的例子中通过创建一个指向document对象的局部变量,就可以通过限制一次全局查找来提升这个函数的性能。

<script type="text/javascript">function updateUI(){
    //缓存全局变量,减少全局变量的查找。这样就减少了话费在作用域上查找的时间,就能提升脚本的性能。改进后的函数只有一次全局查找。
    var doc = document;    var imgs = doc.getElementsByTagName("img");    for(var i=0,len=imgs.length;i<len;i++){
        imgs[i].title = doc.title + "image" +i;
    }    var msg = doc.getElementById("msg");
    msg.innerHTML = "Update complete";
}</script>123456789101112

避免with语句

with语句会创建自己的作用域,因此会增加其中执行的代码的作用域链的长苏。由于额外的作用域链查找,在with语句中执行的代码肯定会比外面执行的代码要慢。 
看下面这个例子:

<script type="text/javascript">/*
大多数情况下,可以使用局部变量完成相同的事情而不必引入新的作用域
 */function updateBody(){
    with(document.body){
        console.log(tagName);//这里将代码的作用域设置到document.body对象中
        innerHTML = "Hello world";
    }
}function updateBody(){
    var body = document.body;
    console.log(body.tagName);//这里将代码的作用域设置到document.body对象中
    body.innerHTML = "Hello world";
}</script>123456789101112131415161718

大多数情况下,可以使用局部变量完成相同的事情而不必引入新的作用域

<script type="text/javascript">function updateBody(){
    var body = document.body;
    console.log(body.tagName);//这里将代码的作用域设置到document.body对象中
    body.innerHTML = "Hello world";
}</script>

上面是我整理给大家的JS开发中最实用的问题解答,希望今后会对大家有帮助。

相关文章:

重点解答js的几种提交方式

详细讲解JS中Windows对象课程

如何在3f1c4e4b6b16bbbd69b2ee476dc4f83a2cacc6d41bbb37262a98f745aa00fbf0标签中一样可以使用el表达式

위 내용은 JS 개발 관련 질문에 대한 가장 실용적인 답변의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.