JavaScript的隐藏功能?[关闭]

2020/10/03 19:21 · javascript ·  · 0评论

您认为每个程序员都应该知道JavaScript的哪些“隐藏功能”?

在看到以下问题的答案的出色质量之后,我认为是时候向它询问JavaScript了。

尽管JavaScript可以说是目前最重要的客户端语言(只需问问Google),但令人惊讶的是,大多数Web开发人员几乎没有意识到它的强大功能。

您无需为函数定义任何参数。您可以只使用函数的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

我可以引用道格拉斯·克罗克福德(Douglas Crockford)的绝妙著作《JavaScript:The Good Parts》中的大部分
内容

但我会举一个你,总是使用===!==替代==!=

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

==不是可传递的。如果使用===它,则所有这些语句都将为false。

函数是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

函数式编程技术可用于编写精美的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();
}

您可以使用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

为变量分配默认值

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

var a = b || c;

a变量将获得的价值c只有当bfalsy(如果是nullfalseundefined0empty string,或NaN),否则a将获得的价值b

如果您想在不提供参数的情况下为参数提供默认值,这通常在函数中很有用:

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

事件处理程序中的IE后备示例:

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

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

debugger声明

描述:§12.15调试器语句

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

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

如果调试器存在或处于活动状态,它将导致该调试器立即中断,就在那条线上。

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

多行字符串文字

描述:§7.8.4字符串文字

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

您必须要小心,因为旁边的字符\ 必须是行终止符,\例如,如果在后面有一个空格,则代码看起来完全相同,但是会产生一个SyntaxError

JavaScript没有块作用域(但是它有闭包,所以我们甚至称它为?)。

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

您可以使用[]而不是访问对象属性.

这使您可以查找与变量匹配的属性。

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漏洞。

如果您要查找有关给定主题的不错的JavaScript参考,请在查询中包含“ mdc”关键字,您的第一个结果将来自Mozilla开发人员中心。我没有随身携带任何离线参考书或书籍。我总是使用“ mdc”关键字技巧来直接获得所需的内容。例如:

Google:javascript数组排序mdc

(大多数情况下,您可以省略“ javascript”)

更新: Mozilla开发人员中心已重命名为Mozilla开发人员网络“ mdc”关键字技巧仍然有效,但是很快我们可能不得不开始使用“ mdn”了

也许对某些人有些明显...

安装Firebug并使用console.log(“ hello”)。与使用随机alert();相比,这要好得多,我记得几年前就做了。

私人方法

对象可以具有私有方法。

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());

在Crockford的“ Javascript:好的部分”中也提到过:

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.

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

fn = function(x){
   // ...
}

fn.foo = 1;

fn.next = function(y){
  //
}

我不得不说自我执行功能。

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

由于Javascript没有块作用域,因此,如果要定义局部变量,可以使用自执行函数:

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

在此,myvaris不会干扰或污染全局范围,并且在函数终止时会消失。

知道一个函数需要多少个参数

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

知道函数接收了多少个参数

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

Here are some interesting things:

  • Comparing NaN with anything (even NaN) is always false, that includes ==, < and >.
  • NaN Stands for Not a Number but if you ask for the type it actually returns a number.
  • Array.sort can take a comparator function and is called by a quicksort-like driver (depends on implementation).
  • Regular expression "constants" can maintain state, like the last thing they matched.
  • 一些JavaScript的版本允许你访问$0$1$2对正则表达式的成员。
  • null与众不同。它既不是对象,布尔值,数字,字符串,也不是undefined有点像“替代” undefined(注:typeof null == "object"
  • 在最外面的上下文中,this产生否则无法命名的[Global]对象。
  • 使用声明变量var,而不是仅仅依赖于变量的自动声明,这为运行时提供了优化访问该变量的真正机会
  • with构造将破坏这种优化
  • 变量名称可以包含Unicode字符。
  • JavaScript正则表达式实际上不是正则表达式。它们基于Perl的正则表达式,并且可以使用需要非常长的时间进行评估的先行构造表达式。
  • 可以标记块并将其用作的目标break可以标记循环并将其用作的目标continue
  • 数组不稀疏。设置原本为空的数组的第1000个元素应使用填充undefined(取决于实施情况)
  • if (new Boolean(false)) {...} 将执行{...}
  • Javascript的正则表达式引擎是特定于实现的:例如,可以编写“非便携式”正则表达式。

[为了回应好评,进行了一些更新;请参阅评论]

我知道我晚会晚了,但是我简直不敢相信+除了“将任何内容转换为数字”之外,还没有提到运营商的有用性。也许这就是隐藏功能的程度了吗?

// 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;

扩展方法在JavaScript ”通过原型属性。

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

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

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

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

var obj = { prop1: 42, prop2: 43 };

obj.prop2 = undefined;

for (var key in obj) {
    ...

属性prop2仍将是迭代的一部分。如果您想完全摆脱prop2,应该改为:

delete obj.prop2;

当您遍历属性prop2时,prop2属性将不再显示。

with

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

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

var user = 
{
   fname: 'Rocket', 
   mname: 'Aloysus',
   lname: 'Squirrel', 
   city: 'Fresno', 
   state: 'California'
};

// ...

with (user)
{
   mname = 'J';
   city = 'Frostbite Falls';
   state = 'Minnesota';
}

艾伦风暴指出,这可能是有点危险:如果用作上下文对象不具有的属性之一被分配到,它会在外部范围加以解决,有可能产生或覆盖全局变量。如果您习惯于编写代码以使用带有默认值或空值的属性未定义的对象,则这特别危险:

var user = 
{
   fname: "John",
// mname definition skipped - no middle name
   lname: "Doe"
};

with (user)
{
   mname = "Q"; // creates / modifies global variable "mname"
}

因此,避免将with语句用于此类分配可能是一个好主意

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

方法(或函数)可以在其设计使用的类型之外的对象上调用。在自定义对象上调用本机(快速)方法非常好。

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

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

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

该代码之所以有效,是因为listNodes定义了足够多的类似数组的属性(length,[]运算符)供sort()

原型继承(由Douglas Crockford推广)彻底改变了您对Java脚本中的负载进行思考的方式。

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'

有人会说这是一个品味问题,但是:

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";

也适用于递归:)

数字也是对象。因此,您可以做一些很酷的事情,例如:

// 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

JavaScript中的闭包如何(类似于C#v2.0 +中的匿名方法)。您可以创建一个创建函数或“表达式”的函数。

关闭示例

//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);

You can also extend (inherit) classes and override properties/methods using the prototype chain spoon16 alluded to.

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

之后,我们创建一个Dog类,该类扩展Pet并再次覆盖.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

这个问题的两个答案都是从Ray Djajadinata的一篇很棒的MSDN文章中修改的代码

您可能会根据类型捕获异常。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
}

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

从我头顶上...

功能

arguments.callee指向承载“参数”变量的函数,因此可用于递归匿名函数:

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 {/*...*/}
})

对象

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

//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);
}

弦乐

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"

您通常可以使用对象而不是开关。

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);

遍历对象的属性时,请确保使用hasOwnProperty方法:

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

这样做是为了使您仅访问anObject直接属性,而不使用原型链中的属性。

具有公共接口的私有变量

它使用了带有自调用函数定义的巧妙技巧。返回对象内部的所有内容都可以在公共接口中使用,而其他所有内容都是私有的。

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());
本文地址:http://javascript.askforanswer.com/javascriptdeyincanggongnengguanbi.html
文章标签: ,  
版权声明:本文为原创文章,版权归 javascript 所有,欢迎分享本文,转载请保留出处!

文件下载

老薛主机终身7折优惠码boke112

上一篇:
下一篇:

评论已关闭!