Javascript Tips(2)

继续讲解javascript的一些小技巧及注意事项。

10. if else vs switch

switch(true) {
  case (y < 20):
    //
    break;
  case (y < 60):
    //
    break;
  case (y < 130):
    //
    break;
}

相当于:

if (y < 20) {
    // ...
} else if (y < 60) {
    // ...
} else if ( y < 130) {
    // ...
}

switch提供了另一种写法,可以在case中添加表达式。 但测试比较看,if else 的效率更高。

当然,还有一种更加简洁的方法,如果为了得到一个变量,直接用二元表达式即可(该表达式也可以添加多个):

var browser = isChrome ? "chrome" :
              isSafari ? "safari" :
              isFirefox ? "firefox" :
              null;

参考:

  1. MDN Web
  2. Jsperf
  3. Stackoverflow

11. 自定义Title tooltip

思路: 先获取 Title 的值,再删除Title属性, 然后自定义tooltip

$(document).ready(function() {
    $('[title]').mouseover(function () {
        $this = $(this);
        $this.data('title', $this.attr('title'));
        // Using null here wouldn't work in IE, but empty string will work just fine.
        $this.attr('title', '');
    }).mouseout(function () {
        $this = $(this);
        $this.attr('title', $this.data('title'));
    });
});

12. FireBug console type

Color Coding:

console
     .log  
     .info
     .debug
     .warn
     .error

上述输出,支持占位符。如

console.log('%d is %s',12.3,'xxxx')
12 is xxxx

Timing and profiling:

console.time("timing foo")
console.timeEnd("timing foo")

参考:firebug log

13.代码性能测试(测试javascript用时)

var t0 = performance.now();
doSomething();
var t1 = performance.now();
console.log("Call to doSomething took " + (t1 - t0) + " milliseconds. ");

14. Apply 、 Call用法

作用:

① 定义一个方法,给另一个处于不同的作用域的对象用。

javascript OOP中,我们经常会这样定义:

function cat(){
}
cat.prototype={
  food:"fish",
  say: function(){
    alert("I love "+this.food);
  }
}
var blackCat = new cat;
blackCat.say();

但是如果我们有一个对象whiteDog = {food:"bone"}, 我们不想对它重新定义say方法,那么我们可以通过callapply用blackCat的say方法:

blackCat.say.call(whiteDog);

所以,可以看出callapply是为了动态改变this而出现的,当一个object没有某个方法,但是其他的有,我们可以借助callapply用其它对象的方法来操作。

② 使用一个方法,定义不同的上下文

//定义一个人,名字为jack
var jack = {
    name : "jack",
    age : 26
}
//定义另一个人,名字为abruzzi
var abruzzi = {
    name : "abruzzi",
    age : 26
}
//定义一个全局的函数对象
function printName(){
    return this.name;
}
//设置printName的上下文为jack, 此时的this为jack
print(printName.call(jack));

③ Apply 、Call区别

第一个参数相同,
后续参数:
Apply为数组 Apply(object, [arguments1, ...]
Call为对象 Call( object, arguments1, ... )

15. Callbacks 可以定义不同的对象

// Callbacks测试  定义不同的callbacks对象,互不影响
var A = $.Callbacks(), B= $.Callbacks();
var x1 = function(u,s){
  console.log("111h:"+u+"\n"+s);
}
var x2 = function(u){
  console.log("hahhh:"+u);
}
A.add(x1);
B.add(x2);
B.add(x1);
A.fire('ss','jjjj');   // 只输出 x1的
B.fire('BBBB','AAAA') // 输出x2、x1的

16. 继承相关

原型继承:

$.fn.xxx = ...
Create.Object( ... );

17. 闭包

注意下面的例子:

var x = 100;  //定义的全局变量
function d1(){
     var x= 1;
     function d2(){
          return x++;
     }
     return d2;  //加括号与不加括号是不同的。 如果加了括号, d2(),代表执行该函数。 不加,则只是返回函数,并不执行
}
var dd = d1();   //此时代表,先执行d1()函数,返回d2 给了dd。
dd(); //output: 1  此代表,执行d2函数, 即 d2()。 由于d2函数与 x 在同一作用域(d1)下,因此x被保留在内存中。
dd(); //output: 2  全局的x并不会收影响

function func(){

var result = [];
for (var i = 0; i < 10; i++) {
     result[i] = function() { 
          return i; 
     }
}
return result;}
var funcc = func();
funcc[8]() //??
          //  怎么打印出 1,2,3,4,5,6,7,8

改为如下方式,即可。

function func(){
  var result = [];
  for (var i = 0; i < 10; i++) {
    result[i] = (function(i) {
      return function(){
        return i;
      };
    })(i)
  }
  return result;
}
var funcc = func();
funcc[8]() //??

使用代理(javascript循环添加事件):

function delegate(fn,params,obj){  
    return function(){  
        fn.call(obj||window,params);  
    }  
}   

18.javascript作用域

JavaScript中的函数运行在它们被定义的作用域里,而不是它们被执行的作用域里.

19.event.srcElement 与 Event.target

  • event.srcElement in Internet Explorer
  • event.target in most other browsers.

20. javascript的比较符

== 与 === 区别: ==只比较值相等, === 对比 值 + 类型 相等
注: 数组 与 对象, 不能直接比较。 如需比较相等,一般需转换为字符串。 JSON.stringify(obj1) = JSON.stringify(obj2)

21. javascript中 逗号 与 分号 区别

逗号: 连续定义, 逗号运算符会计算前后两个表达式,然后返回右边表达式的计算结果
分号:连接前后两个语句。仍返回最后一个表达式的结果。
场景: 逗号是用来分隔参数什么的,而分号是用来分隔语句的。
注: 对象字面量间,只能用 逗号, 而不能用分号。

22. $(this) 与 this

通常,$(this) 是把当前对象,转换成 jquery的对象。如果含有多个相同的 dom,则会返回一个数组。 this为当前作用域的对象。

$(this)[0] === this
$("#myDiv")[0] === document.getElementById("myDiv");

23.判断是否为数字的最快最准确方法

function isNumber(n) {
  return !isNaN(parseFloat(n)) && isFinite(n);
}

24. null 和 0

null 和 0 做 if 判断时,都会返回 false。 如何区分? 用parseInt( n ) === n 即可 。 或 typeof n === 'number'

25. isNaN 与 Type of

用于判断是否为Number时, type of n == 'number' 的效率 要比 isNaN 高很多。 但type of NaN 也会返回 ‘number’

26.自定义事件

用on \ bind 绑定一个事件, 在用 trigger触发。 目的:将订阅者 与 发布者 解耦

27. break 、 continue、 return 区别

break: 跳出循环
continue: 跳出这次
return: 跳出整个
一个Function中,一旦遇到 Return,则会终止执行,不管在嵌套在循环内还是if内。

28.判断javascript对象是否存在

1. 如果只判断对象是否存在,推荐使用第五种写法。

if (typeof myObj == "undefined") {
  var myObj = { };
}

2. 如果除了对象是否存在,还要判断对象是否有null值,推荐使用第一种写法。

if (!myObj) {
  myObj = { };
}

29. Promise 异步编程

目的: 解决ajax请求,用请求后的数据再去请求,这种联调的方式导致的异步问题及不清晰代码导致的不易读问题
形式:

getData(xxx).then(do something).then(do something)

用Promise执行的ajax顺序请求,看起来比多重嵌套的异步写法更易读:

var getData1 = $.ajax('/json1');

var getData2 = function(data1){
  return $.ajax('/json2?'+data1);
};

getData1
  .then(getData2)
  .then(function(data2){
    console.log('Get', data2);
  });

原理(利用原型链):

var getData = function(){};  
getData.prototype.then = function(){};

30. 生成唯一id

/**
powred by mOxie
当前时间戳+五位随机数,唯一性99.99% (除非两个人在同一毫秒内获取到0~65535间相同的随机数)

@method guid
@static
@param {String} prefix to prepend (by default 'o' will be prepended).
@method guid
@return {String} Virtually unique id.
*/
var guid = (function() {
    var counter = 0;
   
    return function(prefix) {
         var guid = new Date().getTime().toString(32), i;

         for (i = 0; i < 5; i++) {
              guid += Math.floor(Math.random() * 65535).toString(32);
         }
        
         return (prefix || 'o_') + guid + (counter++).toString(32);
    };
}());

31. 不要使用 for…in 来遍历数组

for...in...本是用来遍历对象的,且使用此方法,会把对象原型链下的所有属性都会遍历一遍。如果只想得到对象本身的属性,可以用下述方法:

var obj = {a:1, b:2};
Object.setPrototypeOf(obj, {c:3});

for (var prop in obj) {
  if( obj.hasOwnProperty( prop ) ) {
    console.log(obj[prop]); // 只会输出 1, 2
  } 
}

// 亦可使用 Object.getOwnPropertyNames() 获取对象本身的所有属性
Object.getOwnPropertyNames(obj) // ['a','b']

之所以不建议用它来遍历数组,是因为数组具有顺序性,使用 for…in 不能保证数组的顺序性。建议使用 for循环, Array.prototype.forEach(), 或者ES6新方法 for…of

32. className VS classList

给DOM添加类名有两种方法:

element.className = "foo bar";

或者

element.classList.add('foo');
element.classList.add('bar');

而经测试,classList 由于不会因为重新计算dom而耗费多余性能,其要比 className 要快,这在添加多个元素的动画效果时,会非常明显。但目前只支持到IE10+。测试结果可看 jsPerf