JavaScript-Tips

偶然看到JavaScript Tips。这个项目是天天分享一个JavaScript Tip,受到这个项目的启发,我也打算天天更新一个小Tip,我会参考原文,若是以为写的很不错,我也会仅仅充当翻译工,可是每个Tip都会加入我本身的思考,思考若是是我来写这个Tip的话,我会怎么写。git

经验不足,能力有限,还望轻拍。程序员

2016-01-07 至 2016-01-16:github

2016-01-26:数组去重

var arr = ["abc", "undefined","abc","xyz"];
var arr2 = arr.filter(function(item, index){
    return arr.indexOf(item) === index;
});
console.log(arr2);  // ["abc", "undefined", "abc", "xyz"]

上面这种去重方式使用到了indexOf这个函数,返回第一次查找到该元素的索引,用来与该元素在数组中的位置进行比较数组

关键词:数组去重indexOf闭包

2016-01-25: 当即执行函数

(function(name){
    console.log(name);
})("sunyuhui");

上面这种写法就是当即执行函数(更准确的叫法是当即执行匿名函数)。其中包含了两个知识点:框架

  1. 匿名函数的声明。
  2. 函数声明以后使用()能够执行函数。

使用当即执行函数的好处是能够避免函数里定义的变量污染全局变量。由于JS里没有局部变量这个概念(ES2015开始有局部变量的概念)。因此在须要使用到一些比较短的代码同时又不但愿污染全局变量的时候,就可使用当即执行函数函数

关键词:当即执行函数性能

2016-01-24: 使用===代替==

=====的区别在于===不会进行类型转换,而==会进行类型转换。测试

console.log('1' == 1);      // true
console.log('1' === 1);     // false

在使用时我建议使用===,至少我在工做中是这样的,不过也有可能会有须要使用==的场景,这里须要注意一下。优化

关键词:=====

2016-01-23: 将String类型的数字快速转换为Number类型的数字

一般的作法是使用Number().

var a = '1';
var b = Number(a);
console.log(b + ' ' +typeof b);  // 1 number

这里介绍一种更加简单的方法。

var a = '1';
var b = +a;
console.log(b + ' '+ typeof b); // 1 number

使用运算符+能够快速转换为Number类型的数字。

关键词:StringNumber

2016-01-22: 清空一个数组

清空数组一般的作法是直接使用arr = []的方式。不过这里想介绍另外一种方式:使用属性length

var a = [1,2,3];
a.length = 0;
console.log(a);     // []

其实我在2016-01-07:往数组中插入一个元素就提到过,JS中直接操做数组的length属性,会影响到数组具体的内容。这里咱们再来看看这两种清空数组的方式 有什么不一样。

var a = [1,2,3];
var b  = a;
a = [];
console.log(a);     // []
console.log(b);     // [1,2,3]

var c = [1,2,3,4];
var d = c;
c.length = 0;
console.log(c);     // []
console.log(d);     // []

使用将属性length设置为零的方式会把从原数组复制的数组也清空,而使用arr = []的方式则不会。

关键词:length清空数组

2016-01-21:链式调用对象实例的方法

实现链式调用一般是在jQuery或者其余一些框架里,那咱们如何在原生的JavaScript里实现链式调用呢?

function Person(name){
    this.name = name;
    this.setName = function(name){
        this.name = name;
        return this;
    }
    this.sayName = function(){
        console.log(this.name);
        return this;
    }
}
var person = new Person("sunyuhui");
person.sayName().setName("sunyuhui2").sayName();// "sunyuhui" "sunyuhui2"

关键词:链式调用this

2016-01-20: 使用concat拼接字符串

拼接字符串一般的作法是使用加号+,如c = '1' + '2';

使用加号+的前提是要确保参数都是字符串,万一有参数不是字符串,就得不到指望的结果:

var a = 1;
var b = 2;
var c = '3';
var d = a+b+c;
console.log(d);     //"33"

这种时候可使用concat函数.

var a = 1;
var b = 2;
var c = '3';
var d = ''.concat(a,b,c);
console.log(d);         //"123"

注意:Array也有concat函数,要注意区分。

关键词: concat

2016-01-19: 函数嵌套时的做用域

var a = 1;
function get(){
  console.log(a);
}
get();    //1

function get2(){
  var a = 2;
  console.log(a);
}
get2();   //2

上面代码的输出结果你们确定明白,可是下面的代码呢?

var a = 1;
function get(){
  console.log(a);
}

function get3(){
  var a = 2;
  get();
}
get3(); //1

输入结果为1的缘由是因为函数嵌套时,做用域是在定义时决定的,而不是调用时决定的。函数get是在全局做用域下定义的,而运行是在函数get3的做用域下定义的,获取变量a的值是在全局做用域下获取的,而不是在函数get的做用域里获取的。

关键词: 嵌套做用域

2016-01-18: new这个关键字到底作了什么

function Person(name,age){
    this.name = name;
    this.age = age;
}
var person1 = new Person("sunyuhui", "22");

咱们以上面的代码为例说明new这个关键字作了哪些事。

  1. 建立一个新对象person1
  2. 将构造函数Person的做用域赋给person1,这样对象person1就能够访问构造函数Person里的代码。this就指向对象person1
  3. 执行构造函数里的代码,对象personnameage分别被赋值为sunyuhui22
  4. 返回新对象,这样咱们就获得了对象person

关键词: new

2016-01-17: 递归函数的优化

典型的递归函数是求一个数的阶乘:

function getTotal(num){
    if (num<2){
        return 1;
    } else {
        return num * getTotal(num-1);
    }
}
console.log(getTotal(3));       // 6

这里说到的优化主要指的是函数在调用自身的时候能够不用指定函数名,防止函数被从新赋值致使调用出错。

function getTotal(num){
    if (num<2){
        return 1;
    } else {
        return num * arguments.callee(num-1);
    }
}
console.log(getTotal(3));       // 6

其中arguments指向函数的参数,arguments.callee指向正在执行的函数,

关键词: 递归arguments.callee

2016-01-16: 注意不要破坏原型链

在【2016-01-15:实现JS中的继承】中我提到Man.prototype=的形式会破坏构造函数的原型链。那在这种状况下咱们有什么办法不破坏吗?

固然有

function Person(name){
    this.name = name;
}
Person.prototype = {
    //constructor: Person,
    getName: function(){
        return this.name;
    }
}
var man = new Person("sunyuhui");
console.log(man.constructor === Person);      // false

每个构造函数的原型都有一个construtor属性,指向构造函数自己,上面这种设置原型的方式至关于重写了原型,却没有为原型的constructor属性赋值,就破坏了Person的原型链。将注释的部分取消注释就ok了。

关键词:constructor原型链

2016-01-15: 实现JS中的继承

function Person (name,age,pro){
    this.name = name;
    this.age = age;
    this.pro = pro;
}
Person.prototype.getPro = function(){
    return this.pro;
}
function Man (name,age,pro){
    this.name = name;
    this.age = age;
}
Man.prototype = new Person("sunyuhui","22","fe");
var man1 = new Man("sunyuhui2","23");
console.log(man1.name);             // sunyuhui2
console.log(man1.age);              // 23
console.log(man1.getPro());         // fe

将构造函数Person的实例赋予构造函数Man的原型,这样就实现了ManPerson的继承,从中能够看到man1的属性name和属性age覆盖了继承的属性,而属性fe被来自于继承。

注意:Man.prototype = ***的这种形式会修改原型链,使Man的实例对象的constructor属性不为Man.这个点之后单独说。

关键词: 继承

2016-01-14: 测试JS代码块性能

说到console咱们一般使用console.log()来输出变量或者打印日志,这里介绍一个不太经常使用的console.time()console.timeEnd()

console.time("time test");
var arr = new Array(1000);
for(var i=0;i<arr.length;i++){
    arr[i] = {};
}
console.timeEnd("time test");  // time test: 4.037ms

须要注意console.time()console.timeEnd()的参数须要保持相同。

2016-01-13:检测数据类型

咱们一般会使用typeofinstanceofisArray来检测数据的类型,这里我介绍一种万能型的:调用ObjecttoString方法。

function a(){console.log("yes");}
console.log(Object.prototype.toString.call(a)); // [object Function]
var b = 123;
console.log(Object.prototype.toString.call(b)); // [object Number]
var c = "sunhui";
console.log(Object.prototype.toString.call(c)); // [object String]
var d = true;
console.log(Object.prototype.toString.call(d)); // [object Boolean]
var e = [1,2,3];
console.log(Object.prototype.toString.call(e)); // [object Array]
var f;
console.log(Object.prototype.toString.call(f)); // [object Undefined]
var g = null;
console.log(Object.prototype.toString.call(g)); // [object Null]
var h = {"name":"sunyuhui"};
console.log(Object.prototype.toString.call(h)); // [object Object]

使用call调用ObjecttoString方法,将会返回一个遵循[object NativeConstructorName]格式的字符串。其中NativeConstructorName指的就是变量的构造函数名。

2016-01-12: 变量和函数的提早声明

在JavaScript里 变量声明 是让系统知道这个变量存在,而变量定义是给这个变量赋值。变量声明会被提早到顶部,而 变量定义 不会。

console.log(a);   // "ReferenceError: a is not defined"

上面的代码报变量没有定义的错误。

console.log(a);   // undefined
var a;

上面的代码提示undefined,这说明咱们对a的声明被提早了。再来看:

console.log(a); // undefined
var a = 3;

咱们在这里对a同时作了声明定义两个操做,从结果来看,只有声明被提早了,而定义却没有。

接下来看函数:

getName();                  // "sunyuhui"
function getName(){
    console.log("sunyuhui");
}

结果说明咱们对函数的声明被提早了。

getAge();                       // "TypeError: getAge is not a function"
var getAge = function(){
    console.log(22);
}

事实上,第一种方式叫函数声明,这种方式能被提早到顶部,第二种方式叫函数表达式,不会被提早。

关键词:声明、 提早

2016-01-11:判断对象中是否存在某个属性

咱们常常须要用到对象中的某个属性,为了保证程序的健壮性,在使用以前咱们须要判断这个属性是否存在,能够用if来判断

if(obj[property] !== undefined){
    // do something
}

更方便的方式是使用这两个方法:hasOwnPropertyin

function Person (name) {
    this.name  = name;
}
Person.prototype.age = '22';

var person = new Person("sunhui");
console.log(person.hasOwnProperty('name'));    // true
console.log("name" in person);                // true

console.log(person.hasOwnProperty('age'));    // false
console.log("age" in person);                 // true

hasOwnPropertyin均可以用来判断某个属性是否存在于对象中,区别就是hasOwnProperty不能搜索到从原型链继承的属性,而in能够。

关键词:hasOwnPropertyin原型链

2016-01-10:对象数组根据某个属性排序

function compare(prop){
    return function(obj1,obj2){
        var prop1 = obj1[prop];
        var prop2 = obj2[prop];
        if(prop1<prop2){
            return -1;
        } else if(prop1>prop2){
            return 1;
        } else {
            return 0;
        }

    }
}
var arr = [
    {"name":"bsunhui","age":"23"},
    {"name":"csunhui","age":"22"}
];
arr.sort(compare("age"));
console.log(arr);                   //[{"name":"csunhui","age":"22"},{"name":"bsunhui","age":"23"}]
arr.sort(compare("name"));
console.log(arr);                   //[{"name":"bsunhui","age":"23"},{"name":"csunhui","age":"22"}]

compare函数返回了一个闭包,这样就能够根据传入的属性进行排序。当比较字符串时,最好用 localeCompare()方法来比较字符串(字符会按字母表的先后位置关系比较),不然比较的字符的是ascii码。注意数字字符串会被隐式转换为数值类型。

关键词:闭包对象数组按属性排序字符串比较

2016-01-09: 为数组或者单个元素应用相同的函数

举例:

var a = "sun";
var b = ["sun","hui"];

如今须要将a转换成大写,将b中的每一个元素都转换成大写,一般的作法是:

console.log(a.toUpperCase());               // "SUN"
for(var i=0;i<b.length;i++){
    console.log(b[i].toUpperCase());      // "SUN" "HUI"
}

这种作法至关于写了两套代码,看看下面的方式。

function toUpper(words){
    var elements = [].concat(words);
    for(var i=0;i<elements.length;i++){
        console.log(elements[i].toUpperCase());
    }
}

toUpper("sun");                             // "SUN"
toUpper(["sun","hui"]);                     // "SUN" "HUI"

关键点是:concat的用法,str.concat(arg)的参数arg若是是一个数组会把数组里的(不是嵌套数组的)每一项都拿出来做为新数组的元素。

关键词:concat

2016-01-08:null和undefined的区别

  • undefined表示变量没有被声明或者声明了可是没有被赋值。
  • null表示这个变量不是一个值。
  • JavaScript里没有被赋值的变量默认为undefined
  • JavaScript里不会把一个值设置为null,只有程序员本身才能把一个值设置为null
  • 在JSON里undefined不可用,而null是可用的。
  • type of undefined的值为undefined
  • type of null的值为object
  • nullundefined的布尔值都为false。
  • null == undefined的值为true
  • null === undefined的值为false

关键词:nullundefined

2016-01-07:往数组中插入一个元素

通常而言,对数组元素的操做咱们会用到这些函数:pushpopunshiftshiftsplice

var a = [1, 2, 3];
    a.push(4);
    console.log(a);     //[1,2,3,4]
    a.pop();                    
    console.log(a);     //[1,2,3]
    a.unshift(5);
    console.log(a);     //[5,1,2,3]
    a.shift();
    console.log(a);     //[1,2,3]
    a.splice(0,2,6);
    console.log(a)      //[6,3]

我在这里想介绍的是利用length属性来进行数组元素的增减。

var a = [1, 2, 3];
    a.length = 1;
    console.log(a);     //[1]
    a.length = 5;
    console.log(a);     //[1,undefined,undefined, undefined, undefined]
    a = [1,2,3];
    a.splice(a.length/2,0,4,5,6);       //在数组的中间插入元素
    console.log(a);     //[1,4,5,6,2,3]

值得注意的是以上全部方法都改变了原数组。咱们再来一个:

var a = [1,2,3];
    var b = [4,5].concat(a);
    console.log(a);     //[1,2,3]
    console.log(b);     //[4,5,1,2,3]

使用concat来增长数组元素就会返回一个新数组,不会改变原数组。

关键词:数组length

相关文章
相关标签/搜索
本站公众号
   欢迎关注本站公众号,获取更多信息