首页 文章

JavaScript的隐藏功能? [关闭]

提问于
浏览
312

What "Hidden Features" of JavaScript do you think every programmer should know?

在看到以下问题的答案的优秀品质后,我认为是时候向它询问JavaScript了 .

即使JavaScript现在可以说是最重要的客户端语言(只是问Google),但令人惊讶的是,大多数Web开发人员都很少理解它的实力 .

30 回答

  • 23

    你没有't need to define any parameters for a function. You can just use the function' s arguments 类似数组的对象 .

    function sum() {
        var retval = 0;
        for (var i = 0, len = arguments.length; i < len; ++i) {
            retval += arguments[i];
        }
        return retval;
    }
    
    sum(1, 2, 3) // returns 6
    
  • 51

    我可以引用Douglas Crockford的大部分优秀书籍JavaScript: The Good Parts .

    但我会为你拿一个,总是使用 ===!== 而不是 ==!=

    alert('' == '0'); //false
    alert(0 == ''); // true
    alert(0 =='0'); // true
    

    == 不是传递性的 . 如果您使用 === ,则会按预期为所有这些语句提供false .

  • 43

    我不得不说自动执行功能 .

    (function() { alert("hi there");})();
    

    因为Javascript doesn't have block scope,如果要定义局部变量,可以使用自执行函数:

    (function() {
      var myvar = 2;
      alert(myvar);
    })();
    

    这里, myvar 不会干扰或污染全局范围,并在函数终止时消失 .

  • 41

    函数是JavaScript中的一等公民:

    var passFunAndApply = function (fn,x,y,z) { return fn(x,y,z); };
    
    var sum = function(x,y,z) {
      return x+y+z;
    };
    
    alert( passFunAndApply(sum,3,4,5) ); // 12
    

    Functional programming techniques can be used to write elegant javascript .

    特别是,函数可以作为参数传递,例如Array.filter()接受回调:

    [1, 2, -1].filter(function(element, index, array) { return element > 0 });
    // -> [1,2]
    

    您还可以声明仅存在于特定函数范围内的“私有”函数:

    function PrintName() {
        var privateFunction = function() { return "Steve"; };
        return privateFunction();
    }
    
  • 42

    您可以使用 in 运算符检查对象中是否存在键:

    var x = 1;
    var y = 3;
    var list = {0:0, 1:0, 2:0};
    x in list; //true
    y in list; //false
    1 in list; //true
    y in {3:0, 4:0, 5:0}; //true
    

    如果您发现对象文字太丑陋,可以将它与无参数函数提示结合起来:

    function list()
     { var x = {};
       for(var i=0; i < arguments.length; ++i) x[arguments[i]] = 0;
       return x
     }
    
     5 in list(1,2,3,4,5) //true
    
  • 31

    Assigning default values to variables

    您可以在赋值表达式中使用逻辑或运算符||来提供默认值:

    var a = b || c;
    

    仅当 b 为假( nullfalseundefined0empty stringNaN )时, a 变量才会获得 c 的值,否则 a 将获得 b 的值 .

    当你想在没有提供的情况下给参数提供默认值时,这通常在函数中很有用:

    function example(arg1) {
      arg1 || (arg1 = 'default value');
    }
    

    事件处理程序中的IE回退示例:

    function onClick(e) {
        e || (e = window.event);
    }
    

    以下语言功能已经使用了很长时间,所有JavaScript实现都支持它们,但直到ECMAScript 5th Edition它们才是规范的一部分:

    The debugger statement

    描述于:§12.15调试器语句

    此语句允许您通过以下方式以编程方式在代码中放置断点:

    // ...
    debugger;
    // ...
    

    如果调试器存在或处于活动状态,它将导致它立即在该行上断开 .

    否则,如果调试器不存在或处于活动状态,则此语句没有可观察到的影响 .

    Multiline String literals

    描述于:§7.8.4字符串文字

    var str = "This is a \
    really, really \
    long line!";
    

    你必须要小心,因为 \ 旁边的字符必须是行终止符,如果你在_1035186之后有一个空格,例如,代码看起来完全一样,但它会引发一个 SyntaxError .

  • 79

    JavaScript does not have block scope(但它有closure所以让我们称它为偶数?) .

    var x = 1;
    {
       var x = 2;
    }
    alert(x); // outputs 2
    
  • 99

    You can access object properties with [] instead of .

    这允许您查找与变量匹配的属性 .

    obj = {a:"test"};
    var propname = "a";
    var b = obj[propname];  // "test"
    

    您还可以使用它来获取/设置名称不是合法标识符的对象属性 .

    obj["class"] = "test";  // class is a reserved word; obj.class would be illegal.
    obj["two words"] = "test2"; // using dot operator not possible with the space.
    

    有些人不知道这一点并最终使用这样的 eval() ,这是一个非常糟糕的主意:

    var propname = "a";
    var a = eval("obj." + propname);
    

    这很难阅读,更难找到错误(不能使用jslint),执行速度较慢,并且可能导致XSS攻击 .

  • 91

    如果您正在使用Google搜索关于给定主题的合适JavaScript引用,请在查询中包含“mdc”关键字,并且您的第一批结果将来自Mozilla开发人员中心 . 我没有随身携带任何离线参考书或书籍 . 我总是使用“mdc”关键字技巧来直接找到我正在寻找的东西 . 例如:

    谷歌:javascript array sort mdc
    (在大多数情况下,你可以省略"javascript")

    Update: Mozilla Developer Center 已更名为Mozilla Developer Network . "mdc"关键字技巧仍然有效,但很快我们可能需要 start using "mdn" instead .

  • 83

    对某些人来说可能有点明显......

    安装Firebug并使用console.log("hello") . 比使用随机警报()更好,我记得几年前做了很多 .

  • 60

    Private Methods

    对象可以具有私有方法 .

    function Person(firstName, lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    
        // A private method only visible from within this constructor
        function calcFullName() {
           return firstName + " " + lastName;    
        }
    
        // A public method available to everyone
        this.sayHello = function () {
            alert(calcFullName());
        }
    }
    
    //Usage:
    var person1 = new Person("Bob", "Loblaw");
    person1.sayHello();
    
    // This fails since the method is not visible from this scope
    alert(person1.calcFullName());
    
  • 144

    在Crockford的“Javascript:The Good Parts”中也提到过:

    parseInt() 很危险 . 如果你传递一个字符串而没有通知它正确的基数,它可能会返回意外的数字 . 例如 parseInt('010') 返回8而不是10.将基数传递给parseInt使其正常工作:

    parseInt('010') // returns 8! (in FF3)
    parseInt('010', 10); // returns 10 because we've informed it which base to work with.
    
  • 77

    函数是对象,因此可以具有属性 .

    fn = function(x) {
       // ...
    }
    
    fn.foo = 1;
    
    fn.next = function(y) {
      //
    }
    
  • 25

    Know how many parameters are expected by a function

    function add_nums(num1, num2, num3 ){
        return num1 + num2 + num3;
    }
    add_nums.length // 3 is the number of parameters expected.
    

    Know how many parameters are received by the function

    function add_many_nums(){
        return arguments.length;
    }    
    add_many_nums(2,1,122,12,21,89); //returns 6
    
  • 29

    以下是一些有趣的事情:

    • NaN 与任何内容(甚至 NaN )进行比较始终为false,包括 ==<> .

    • NaN 代表不是数字,但如果你要求输入类型,它实际上会返回一个数字 .

    • Array.sort 可以采用比较器功能,并由类似快速排序的驱动程序调用(取决于实现) .

    • 正则表达式"constants"可以保持状态,就像它们匹配的最后一样 .

    • 某些版本的JavaScript允许您在正则表达式上访问 $0$1$2 成员 .

    • null 与众不同 . 它既不是对象,也不是布尔值,数字,字符串,也不是 undefined . 这有点像"alternate" undefined . (注: typeof null == "object"

    • 在最外层的上下文中, this 产生否则无法命名的[Global]对象 .

    • 使用 var 声明变量,而不仅仅依赖于变量的自动声明,使运行时有机会优化对该变量的访问

    • with 构造将破坏此类优化

    • 变量名称可以包含Unicode字符 .

    • JavaScript正则表达式实际上不是常规的 . 它们基于Perl的正则表达式,并且可以构建具有前瞻性的表达式,这些表达式需要花费非常长的时间来进行评估 .

    • 可以标记块并将其用作 break 的目标 . 可以标记循环并将其用作 continue 的目标 .

    • 数组不稀疏 . 设置其他空数组的第1000个元素应该用 undefined 填充它 . (取决于实施)

    • if (new Boolean(false)) {...} 将执行 {...}

    • Javascript 's regular expression engine' s是特定于实现的:例如可以编写"non-portable"正则表达式 .

    [根据好的评论稍作更新;请看评论]

  • 31

    我知道'm late to the party, but I just can' t相信 + 运算符's usefulness hasn' t已被提及超出"convert anything to a number" . 也许这是隐藏一个功能有多好?

    // Quick hex to dec conversion:
    +"0xFF";              // -> 255
    
    // Get a timestamp for now, the equivalent of `new Date().getTime()`:
    +new Date();
    
    // Safer parsing than parseFloat()/parseInt()
    parseInt("1,000");    // -> 1, not 1000
    +"1,000";             // -> NaN, much better for testing user input
    parseInt("010");      // -> 8, because of the octal literal prefix
    +"010";               // -> 10, `Number()` doesn't parse octal literals 
    
    // A use case for this would be rare, but still useful in cases
    // for shortening something like if (someVar === null) someVar = 0;
    +null;                // -> 0;
    
    // Boolean to integer
    +true;                // -> 1;
    +false;               // -> 0;
    
    // Other useful tidbits:
    +"1e10";              // -> 10000000000
    +"1e-4";              // -> 0.0001
    +"-12";               // -> -12
    

    当然,您可以使用 Number() 代替这一切,但 + 运算符更漂亮!

    您还可以通过覆盖原型的 valueOf() 方法为对象定义数字返回值 . 对该对象执行的任何数字转换都不会产生 NaN ,而是 valueOf() 方法的返回值:

    var rnd = {
        "valueOf": function () { return Math.floor(Math.random()*1000); }
    };
    +rnd;               // -> 442;
    +rnd;               // -> 727;
    +rnd;               // -> 718;
    
  • 189

    Extension methods in JavaScript”通过原型属性 .

    Array.prototype.contains = function(value) {  
        for (var i = 0; i < this.length; i++) {  
            if (this[i] == value) return true;  
        }  
        return false;  
    }
    

    这将为所有 Array 对象添加 contains 方法 . 您可以使用此语法调用此方法

    var stringArray = ["foo", "bar", "foobar"];
    stringArray.contains("foobar");
    
  • 120

    要从对象中正确删除属性,应删除该属性,而不是仅将其设置为undefined:

    var obj = { prop1: 42, prop2: 43 };
    
    obj.prop2 = undefined;
    
    for (var key in obj) {
        ...
    

    属性prop2仍然是迭代的一部分 . 如果你想完全摆脱prop2,你应该做:

    delete obj.prop2;
    

    当您遍历属性时,属性prop2将不再出现 .

  • 57

    with .

    它很少使用,坦率地说,很少有用......但是,在有限的情况下,它确实有其用途 .

    例如:对象文字对于在新对象上快速设置属性非常方便 . 但是,如果您需要更改现有对象的一半属性,该怎么办?

    var user = 
    {
       fname: 'Rocket', 
       mname: 'Aloysus',
       lname: 'Squirrel', 
       city: 'Fresno', 
       state: 'California'
    };
    
    // ...
    
    with (user)
    {
       mname = 'J';
       city = 'Frostbite Falls';
       state = 'Minnesota';
    }
    

    Alan Storm指出这可能有点危险:如果用作上下文的对象没有分配其中一个属性,它将在外部范围内解析,可能创建或覆盖全局变量 . 如果您习惯于编写代码来处理具有默认值或空值的属性未定义的对象,那么这将特别危险:

    var user = 
    {
       fname: "John",
    // mname definition skipped - no middle name
       lname: "Doe"
    };
    
    with (user)
    {
       mname = "Q"; // creates / modifies global variable "mname"
    }
    

    因此,避免使用 with 语句进行此类分配可能是个好主意 .

    另请参阅:JavaScript的“with”语句是否有合法用途?

  • 144

    可以在对象上调用方法(或函数),这些方法不是它们设计用于的类型 . 这很适合在自定义对象上调用本机(快速)方法 .

    var listNodes = document.getElementsByTagName('a');
    listNodes.sort(function(a, b){ ... });
    

    此代码崩溃,因为 listNodes 不是 Array

    Array.prototype.sort.apply(listNodes, [function(a, b){ ... }]);
    

    此代码有效,因为 listNodes 定义了 sort() 使用的足够类似数组的属性(length,[]运算符) .

  • 75

    Prototypal inheritance (由道格拉斯·克罗克福德推广)彻底改变了你在Javascript中思考大量内容的方式 .

    Object.beget = (function(Function){
        return function(Object){
            Function.prototype = Object;
            return new Function;
        }
    })(function(){});
    

    这是一个杀手!可惜几乎没有人使用它 .

    它允许您“生成”任何对象的新实例,扩展它们,同时保持(实时)原型继承链接到其他属性 . 例:

    var A = {
      foo : 'greetings'
    };  
    var B = Object.beget(A);
    
    alert(B.foo);     // 'greetings'
    
    // changes and additionns to A are reflected in B
    A.foo = 'hello';
    alert(B.foo);     // 'hello'
    
    A.bar = 'world';
    alert(B.bar);     // 'world'
    
    
    // ...but not the other way around
    B.foo = 'wazzap';
    alert(A.foo);     // 'hello'
    
    B.bar = 'universe';
    alert(A.bar);     // 'world'
    
  • 153

    有人会称之为品味,但是:

    aWizz = wizz || "default";
    // same as: if (wizz) { aWizz = wizz; } else { aWizz = "default"; }
    

    可以将三元运算符链接起来像Scheme(cond ...):

    (cond (predicate  (action  ...))
          (predicate2 (action2 ...))
          (#t         default ))
    

    可写成......

    predicate  ? action( ... ) :
    predicate2 ? action2( ... ) :
                 default;
    

    这非常“实用”,因为它可以在没有副作用的情况下分支您的代码 . 所以代替:

    if (predicate) {
      foo = "one";
    } else if (predicate2) {
      foo = "two";
    } else {
      foo = "default";
    }
    

    你可以写:

    foo = predicate  ? "one" :
          predicate2 ? "two" :
                       "default";
    

    同样适用于递归:)

  • 145

    数字也是对象 . 所以你可以做很酷的事情:

    // convert to base 2
    (5).toString(2) // returns "101"
    
    // provide built in iteration
    Number.prototype.times = function(funct){
      if(typeof funct === 'function') {
        for(var i = 0;i < Math.floor(this);i++) {
          funct(i);
        }
      }
      return this;
    }
    
    
    (5).times(function(i){
      string += i+" ";
    });
    // string now equals "0 1 2 3 4 "
    
    var x = 1000;
    
    x.times(function(i){
      document.body.innerHTML += '<p>paragraph #'+i+'</p>';
    });
    // adds 1000 parapraphs to the document
    
  • 33

    在JavaScript中如何 closures (类似于匿名C#v2.0中的方法 . 您可以创建一个创建函数或"expression"的函数 .

    closures 的示例:

    //Takes a function that filters numbers and calls the function on 
    //it to build up a list of numbers that satisfy the function.
    function filter(filterFunction, numbers)
    {
      var filteredNumbers = [];
    
      for (var index = 0; index < numbers.length; index++)
      {
        if (filterFunction(numbers[index]) == true)
        {
          filteredNumbers.push(numbers[index]);
        }
      }
      return filteredNumbers;
    }
    
    //Creates a function (closure) that will remember the value "lowerBound" 
    //that gets passed in and keep a copy of it.
    function buildGreaterThanFunction(lowerBound)
    {
      return function (numberToCheck) {
        return (numberToCheck > lowerBound) ? true : false;
      };
    }
    
    var numbers = [1, 15, 20, 4, 11, 9, 77, 102, 6];
    
    var greaterThan7 = buildGreaterThanFunction(7);
    var greaterThan15 = buildGreaterThanFunction(15);
    
    numbers = filter(greaterThan7, numbers);
    alert('Greater Than 7: ' + numbers);
    
    numbers = filter(greaterThan15, numbers);
    alert('Greater Than 15: ' + numbers);
    
  • 32

    你也可以 extend (inherit) classes and override properties/methods 使用原型链 spoon16 提到 .

    在下面的示例中,我们创建了一个Pet类并定义了一些属性 . 我们还覆盖从Object继承的.toString()方法 .

    在此之后我们创建一个Dog类,其中 extends Pet and overrides the .toString() 方法再次改变它的行为(多态) . 此外,我们还为子类添加了一些其他属性 .

    在此之后,我们检查继承链以显示Dog仍然是Dog类型,Pet类型和Object类型 .

    // Defines a Pet class constructor 
    function Pet(name) 
    {
        this.getName = function() { return name; };
        this.setName = function(newName) { name = newName; };
    }
    
    // Adds the Pet.toString() function for all Pet objects
    Pet.prototype.toString = function() 
    {
        return 'This pets name is: ' + this.getName();
    };
    // end of class Pet
    
    // Define Dog class constructor (Dog : Pet) 
    function Dog(name, breed) 
    {
        // think Dog : base(name) 
        Pet.call(this, name);
        this.getBreed = function() { return breed; };
    }
    
    // this makes Dog.prototype inherit from Pet.prototype
    Dog.prototype = new Pet();
    
    // Currently Pet.prototype.constructor
    // points to Pet. We want our Dog instances'
    // constructor to point to Dog.
    Dog.prototype.constructor = Dog;
    
    // Now we override Pet.prototype.toString
    Dog.prototype.toString = function() 
    {
        return 'This dogs name is: ' + this.getName() + 
            ', and its breed is: ' + this.getBreed();
    };
    // end of class Dog
    
    var parrotty = new Pet('Parrotty the Parrot');
    var dog = new Dog('Buddy', 'Great Dane');
    // test the new toString()
    alert(parrotty);
    alert(dog);
    
    // Testing instanceof (similar to the `is` operator)
    alert('Is dog instance of Dog? ' + (dog instanceof Dog)); //true
    alert('Is dog instance of Pet? ' + (dog instanceof Pet)); //true
    alert('Is dog instance of Object? ' + (dog instanceof Object)); //true
    

    这个问题的答案都是从great MSDN article by Ray Djajadinata.修改的代码

  • 97

    您可以根据类型捕获异常 . 引自MDC

    try {
       myroutine(); // may throw three exceptions
    } catch (e if e instanceof TypeError) {
       // statements to handle TypeError exceptions
    } catch (e if e instanceof RangeError) {
       // statements to handle RangeError exceptions
    } catch (e if e instanceof EvalError) {
       // statements to handle EvalError exceptions
    } catch (e) {
       // statements to handle any unspecified exceptions
       logMyErrors(e); // pass exception object to error handler
    }
    

    NOTE: 条件catch子句是Netscape(因此也是Mozilla / Firefox)扩展,它不是ECMAScript规范的一部分,因此除特定浏览器外不能依赖它 .

  • 204

    我的头顶......

    Functions

    arguments.callee是指托管“arguments”变量的函数,因此它可用于递归匿名函数:

    var recurse = function() {
      if (condition) arguments.callee(); //calls recurse() again
    }
    

    如果你想做这样的事情,这很有用:

    //do something to all array items within an array recursively
    myArray.forEach(function(item) {
      if (item instanceof Array) item.forEach(arguments.callee)
      else {/*...*/}
    })
    

    Objects

    关于对象成员的一个有趣的事情:它们可以有任何字符串作为其名称:

    //these are normal object members
    var obj = {
      a : function() {},
      b : function() {}
    }
    //but we can do this too
    var rules = {
      ".layout .widget" : function(element) {},
      "a[href]" : function(element) {}
    }
    /* 
    this snippet searches the page for elements that
    match the CSS selectors and applies the respective function to them:
    */
    for (var item in rules) {
      var elements = document.querySelectorAll(rules[item]);
      for (var e, i = 0; e = elements[i++];) rules[item](e);
    }
    

    Strings

    String.split可以将正则表达式作为参数:

    "hello world   with  spaces".split(/\s+/g);
    //returns an array: ["hello", "world", "with", "spaces"]
    

    String.replace可以将正则表达式作为搜索参数,将函数作为替换参数:

    var i = 1;
    "foo bar baz ".replace(/\s+/g, function() {return i++});
    //returns "foo1bar2baz3"
    
  • 373

    您可以在大多数时间使用对象而不是开关 .

    function getInnerText(o){
        return o === null? null : {
            string: o,
            array: o.map(getInnerText).join(""),
            object:getInnerText(o["childNodes"])
        }[typeis(o)];
    }
    

    更新:如果您担心事先评估的效率低下(为什么您在设计程序的早期担心效率?)那么您可以这样做:

    function getInnerText(o){
        return o === null? null : {
            string: function() { return o;},
            array: function() { return o.map(getInnerText).join(""); },
            object: function () { return getInnerText(o["childNodes"]; ) }
        }[typeis(o)]();
    }
    

    键入(或读取)比使用开关或对象更加繁琐,但它保留了使用对象而不是开关的好处,详见下面的注释部分 . 这种风格也使它更容易将它变成适当的“阶级”,一旦它长大了 .

    update2:对于ES.next提出了语法扩展,这就变成了

    let getInnerText = o -> ({
        string: o -> o,
        array: o -> o.map(getInnerText).join(""),
        object: o -> getInnerText(o["childNodes"])
    }[ typeis o ] || (->null) )(o);
    
  • 162

    迭代对象的属性时,请务必使用 hasOwnProperty 方法:

    for (p in anObject) {
        if (anObject.hasOwnProperty(p)) {
            //Do stuff with p here
        }
    }
    

    这样做是为了您只访问 direct properties of anObject ,而不使用原型链下的属性 .

  • 143

    具有公共接口的私有变量

    它使用一个带有自调用函数定义的巧妙小技巧 . 返回的对象内的所有内容都可以在公共界面中使用,而其他所有内容都是私有的 .

    var test = function () {
        //private members
        var x = 1;
        var y = function () {
            return x * 2;
        };
        //public interface
        return {
            setx : function (newx) {
                x = newx;
            },
            gety : function () {
                return y();
            }
        }
    }();
    
    assert(undefined == test.x);
    assert(undefined == test.y);
    assert(2 == test.gety());
    test.setx(5);
    assert(10 == test.gety());
    

相关问题