当前位置: 首页 > news >正文

44道JS难题

国外某网站给出了44道JS难题,试着做了下,只做对了17道。这些题涉及面非常广,涵盖JS原型、函数细节、强制转换、闭包等知识,而且都是非常细节的东西,透过这些小细节可以折射出很多高级的JS知识点。

你可以通过传送门先去测试一下你的水平,然后回来看看我的解析。为了详细解释这些细节,我也查阅了不少资料,弥补了很多JS知识盲点。

1. parseInt 遇上 map


["1", "2", "3"].map(parseInt) // A. ["1", "2", "3"] // B. [1, 2, 3] // C. [0, 1, 2] // D. other 

答案是D。实际上返回的结果是 [1, NaN, NaN] ,因为 parseInt 函数只需要两个参数 parseInt(value, radix) ,而 map 的回调函数需要三个参数 callback(currentValue, index, array)。MDN文档中指明 parseInt 第二个参数是一个2到36之间的整数值,用于指定转换中采用的基数。如果省略该参数或其值为0,则数字将以10为基础来解析。如果该参数小于2或者大于36,则 parseInt 返回 NaN。此外,转换失败也会返回 NaN

现在来分析问题。parseInt("1", 0) 的结果是当作十进制来解析,返回 1parseInt("2", 1) 的第二个参数非法,返回 NaNparseInt("3", 2) 在二进制中,"3"是非法字符,转换失败,返回 NaN

参考资料:

  • MDN: Array.prototype.map()
  • MDN: parseInt

2. 神奇的null


[typeof null, null instanceof Object] // A. ["object", false] // B. [null, false] // C. ["object", true] // D. other 

答案是A。在MDN关于 null 的文档中也特别指出来了,typeof null 的结果是 "object",它是ECMAScript的bug,其实应该是 "null"。但这个bug由来已久,在JavaScript中已经存在了将近二十年,也许永远不会修复,因为这牵扯到太多的Web系统,修复它会产生更多的bug,令许多系统无法正常工作。而 instanceof 运算符是用来测试一个对象在其原型链构造函数上是否具有 prototype 属性,null 值并不是以 Object 原型创建出来的,所以 null instanceof Object 返回 false

参考资料:

  • MDN:null
  • MDN:instanceof
  • CSDN博客:null instanceof Object 求解?

3. 愤怒的reduce


[ [3,2,1].reduce(Math.pow), [].reduce(Math.pow) ] // A. an error // B. [9, 0] // C. [9, NaN] // D. [9, undefined] 

答案是A。MDN文档中关于 Array.prototype.reduce() 写得很清楚:

如果数组为空并且没有提供initialValue, 会抛出TypeError 。如果数组仅有一个元素(无论位置如何)并且没有提供initialValue, 或者有提供initialValue但是数组为空,那么此唯一值将被返回并且callback不会被执行。

参考资料:

  • MDN:Array.prototype.reduce()

4. 该死的优先级


var val = 'smtg';
console.log('Value is ' + (val === 'smtg') ? 'Something' : 'Nothing'); // A. Value is Something // B. Value is Nothing // C. NaN // D. other 

答案是D。实际上输出 "Something",因为 + 的优先级比条件运算符 condition ? val1 : val2 的优先级高。

参考资料:

  • MDN:运算符优先级

5. 神鬼莫测之变量提升


var name = 'World!';
(function () { if (typeof name === 'undefined') { var name = 'Jack'; console.log('Goodbye ' + name); } else { console.log('Hello ' + name); } })(); // A. Goodbye Jack // B. Hello Jack // C. Hello undefined // D. Hello World 

答案是A。看如下MDN官方文档的解释:

在 JavaScript中, functions 和 variables 会被提升。变量提升是JavaScript将声明移至作用域 scope (全局域或者当前函数作用域) 顶部的行为。

这意味着你可以在声明一个函数或变量之前引用它,或者可以说:一个变量或函数可以在它被引用之后声明。

所以,上面的代码与下面这段代码是等价的:

var name = 'World!';
(function () { var name; if (typeof name === 'undefined') { name = 'Jack'; console.log('Goodbye ' + name); } else { console.log('Hello ' + name); } })(); 

参考资料:

  • MDN:变量提升
  • 这篇博客解释的比较详细:js变量提升

6. 死循环陷阱


var END = Math.pow(2, 53); var START = END - 100; var count = 0; for (var i = START; i <= END; i++) { count++; } console.log(count); // A. 0 // B. 100 // C. 101 // D. other 

答案是D。在JavaScript中,2^53 是最大的值,没有比这更大的值了。所以 2^53 + 1 == 2^53,所以这个循环无法终止。

7. 过滤器魔法


var ary = [0,1,2]; ary[10] = 10; ary.filter(function(x) { return x === undefined; }); // A. [undefined x 7] // B. [0, 1, 2, 10] // C. [] // D. [undefined] 

答案是C。看MDN官方文档的描述:

filter 为数组中的每个元素调用一次 callback 函数,并利用所有使得 callback 返回 true 或 等价于 true 的值 的元素创建一个新数组。callback 只会在已经赋值的索引上被调用,对于那些已经被删除或者从未被赋值的索引不会被调用。那些没有通过 callback 测试的元素会被跳过,不会被包含在新数组中。

参考资料:

  • MDN:Array.prototype.filter()

8. 警惕IEEE 754标准


var two = 0.2;
var one = 0.1; var eight = 0.8; var six = 0.6; [two - one == one, eight - six == two] // A. [true, false] // B. [false, false] // C. [true, false] // D. other 

答案是C。JavaScript中采用双精度浮点数格式,即IEEE 754标准。在该格式下,有些数字无法表示出来,比如:0.1 + 0.2 = 0.30000000000000004 ,这不是JavaScript的锅,所有采用该标准的语言都有这个问题,比如:Java、Python等。

参考资料:

  • Wiki:Double-precision floating-point format

9. 字符串陷阱


function showCase(value) { switch(value) { case 'A': console.log('Case A'); break; case 'B': console.log('Case B'); break; case undefined: console.log('undefined'); break; default: console.log('Do not know!'); } } showCase(new String('A')); // A. Case A // B. Case B // C. Do not know! // D. undefined 

答案是C。在 switch 内部使用严格相等 === 进行判断,并且 new String("A") 返回的是一个对象,而 String("A") 则是直接返回字符串 "A"。你也可以参考MDN中对原始字符串和String对象的区分:

Note that JavaScript distinguishes between String objects and primitive string values. (The same is true of Boolean and Numbers.)

String literals (denoted by double or single quotes) and strings returned from String calls in a non-constructor context (i.e., without using the new keyword) are primitive strings. JavaScript automatically converts primitives to String objects, so that it's possible to use String object methods for primitive strings. In contexts where a method is to be invoked on a primitive string or a property lookup occurs, JavaScript will automatically wrap the string primitive and call the method or perform the property lookup.

参考资料:

  • MDN:String

10. 再一次的字符串陷阱


function showCase(value) { switch(value) { case 'A': console.log('Case A'); break; case 'B': console.log('Case B'); break; case undefined: console.log('undefined'); break; default: console.log('Do not know!'); } } showCase(String('A')); // A. Case A // B. Case B // C. Do not know! // D. undefined 

答案显然是A。与上面唯一不同的是没有使用 new 关键字,所以直接返回字符串,实际上,typeof string("A") === "string" 的结果是 true。解释参见第9条的解释。

11. 并非都是奇偶


function isOdd(num) {return num % 2 == 1;
}function isEven(num) {return num % 2 == 0;
}function isSane(num) { return isEven(num) || isOdd(num); } var values = [7, 4, "13", -9, Infinity]; values.map(isSane); // A. [true, true, true, true, true] // B. [true, true, true, true, false] // C. [true, true, true, false, false] // D. [true, true, false, false, false] 

答案是C。-9 % 2 = -1 以及 Infinity % 2 = NaN,求余运算符会保留符号,所以只有 isEven 的判断是可靠的。

12. parseInt小贼


parseInt(3, 8);
parseInt(3, 2); parseInt(3, 0); // A. 3, 3, 3 // B. 3, 3, NaN // C. 3, NaN, NaN // D. other 

答案是D。实际结果是 3, NaN, 3,这个在第一个问题中解释的很清楚了。

13. 数组原型是数组


Array.isArray( Array.prototype )// A. true
// B. false // C. error // D. other 

答案是A。一个鲜为人知的事实:其实 Array.prototype 也是一个数组。这点在MDN文档中提到过。

参考资料:

  • MDN:Array.isArray()

14. 一言难尽的强制转换


var a = [0];
if ([0]) { console.log(a == true); } else { console.log("wut"); } // A. true // B. false // C. "wut" // D. other 

答案是B。这个是JavaScript中强制转换的经典案例,关于强制转换不是一两句话可以跟你说清楚的,我建议你系统性的学习一下,推荐你看看《你不知道的JavaScript-中卷》这本书,如果不舍得买书,github上有英文原版:You-Dont-Know-JS,深入理解之后你就是高手了。

好了,回到当前这个问题。当 [0] 需要被强制转成 Boolean 的时候会被认为是 true。所以进入第一个 if 语句,而 a == true 的转换规则在ES5规范的第11.9.3节中已经定义过,你可以自己详细探索下。

规范指出,== 相等中,如果有一个操作数是布尔类型,会先把他转成数字,所以比较变成了 [0] == 1;同时规范指出如果其他类型和数字比较,会尝试把这个类型转成数字再进行宽松比较,而对象(数组也是对象)会先调用它的 toString() 方法,此时 [0] 会变成 "0",然后将字符串 "0" 转成数字 0,而 0 == 1 的结果显然是 false

参考资料:

  • ES5规范:11.9.3
  • 《你不知道的JavaScript-中卷》

15. 撒旦之子“==”


[]==[]// A. true
// B. false
// C. error
// D. other 

答案是B。ES5规范11.9.3.1-f指出:如果比较的两个对象指向的是同一个对象,就返回 true,否则就返回 false,显然,这是两个不同的数组对象。

参考资料:

  • ES5规范:11.9.3.1
  • 《你不知道的JavaScript-中卷》

16. 加号 VS 减号


'5' + 3;
'5' - 3; // A. "53", 2 // B. 8, 2 // C. error // D. other 

答案是A。"5" + 2 = "52" 很好理解,+ 运算符中只要有一个是字符串,就会变成字符串拼接操作。你不知道的是,- 运算符要求两个操作数都是数字,如果不是,会强制转换成数字,所以结果就变成了 5 - 2 = 3

参考资料:

  • 《你不知道的JavaScript-中卷》,第四章:4.4.2 字符串和数字之间的隐式强制类型转换

17. 打死那个疯子


1 + - + + + - + 1// A. 2
// B. 1 // C. error // D. other 

答案是A。这个只能出现在示例代码中,如果你发现哪个疯子写了这个在生产代码中,打死他就行了。你只要知道 + 1 = 1- 1 = -1,注意符号之间的空格。两个减号抵消,所以最终结果等效于 1 + 1 = 2。或者你也可以在符号之间插入 0 来理解,即 1 + 0 - 0 + 0 + 0 + 0 - 0 + 1,这样你就一目了然了吧!千万别写这样的代码,因为可能会被打死!

18. 淘气的map


var ary = Array(3);
ary[0] = 2; ary.map(function(elem) { return "1"; }); // A. [2, 1, 1] // B. ["1", "1", "1"] // C. [2, "1", "1"] // D. other 

答案是D。实际上结果是 ["1", undefined x 2],因为规范写得很清楚:

map 方法会给原数组中的每个元素都按顺序调用一次 callback 函数。callback 每次执行后的返回值组合起来形成一个新数组。 callback 函数只会在有值的索引上被调用;那些从来没被赋过值或者使用 delete 删除的索引则不会被调用。

参考资料:

  • MDN: Array.prototype.map()

19. 统统算我的


function sidEffecting(ary) { ary[0] = ary[2]; } function bar(a, b, c) { c = 10; sidEffecting(arguments); return a + b + c; } bar(1, 1, 1); // A. 3 // B. 12 // C. error // D. other 

答案是D。实际上结果是 21。在JavaScript中,参数变量和 arguments 是双向绑定的。改变参数变量,arguments 中的值会立即改变;而改变 arguments 中的值,参数变量也会对应改变。

20. 损失精度的IEEE 754


var a = 111111111111111110000;
var b = 1111; console.log(a + b); // A. 111111111111111111111 // B. 111111111111111110000 // C. NaN // D. Infinity 

答案是B。这是IEEE 754规范的黑锅,不是JavaScript的问题。表示这么大的数占用过多位数,会丢失精度,学过计算机组成原理的应该知道是怎么回事。

参考资料:

  • Wiki:Double-precision floating-point format

21. 反转世界


var x = [].reverse;
x();// A. []
// B. undefined
// C. error // D. window 

答案是D。MDN规范关于 reverse 的描述:

reverse 方法颠倒数组中元素的位置,并返回该数组的引用。

而这里调用的时候没有制定数组,所以默认的 this 就是 window,所以最后结果返回的是 window

参考资料:

  • MDN:Array.prototype.reverse()

22. 最小的正值


Number.MIN_VALUE > 0// A. false
// B. true // C. error // D. other 

答案是B。看规范描述吧:

MIN_VALUE属性是 JavaScript 里最接近 0 的正值,而不是最小的负值。

MIN_VALUE的值约为 5e-324。小于 MIN_VALUE
("underflow values") 的值将会转换为 0。

因为 MIN_VALUE是 Number 的一个静态属性,因此应该直接使用: Number.MIN_VALUE,而不是作为一个创建的 Number实例的属性。

参考资料:

  • MDN:Number.MIN_VALUE

23. 谨记优先级


[1 < 2 < 3, 3 < 2 < 1]// A. [true, true]
// B. [true, false]
// C. error
// D. other

答案是A。<>的优先级都是从左到右,所以 1 < 2 < 3 会先比较 1 < 2,这会得到 true,但是 < 要求比较的两边都是数字,所以会发生隐式强制转换,将 true 转换成 1,所以最后就变成了比较 1 < 3,结果显然为 true。同理可以分析后者。

参考资料:

  • MDN:运算符优先级

24. 坑爹中的战斗机


// the most classic wtf
2 == [[[2]]]// A. true // B. false // C. undefined // D. other 

答案是A。根据ES5规范,如果比较的两个值中有一个是数字类型,就会尝试将另外一个值强制转换成数字,再进行比较。而数组强制转换成数字的过程会先调用它的 toString方法转成字符串,然后再转成数字。所以 [2]会被转成 "2",然后递归调用,最终 [[[2]]] 会被转成数字 2

25. 小数点魔术


3.toString();
3..toString();
3...toString();// A. "3", error, error
// B. "3", "3.0", error
// C. error, "3", error
// D. other

答案是C。点运算符会被优先识别为数字常量的一部分,然后才是对象属性访问符。所以 3.toString() 实际上被JS引擎解析成 (3.)toString(),显然会出现语法错误。但是如果你这么写 (3).toString(),人为加上括号,这就是合法的。

26. 自动提升为全局变量


(function() {var x = y = 1; })(); console.log(y); console.log(x); // A. 1, 1 // B. error, error // C. 1, error // D. other 

答案是C。很经典的例子,在函数中没有用 var 声明变量 y,所以 y 会被自动创建在全局变量 window下面,所以在函数外面也可以访问得到。而 x 由于被 var 声明过,所以在函数外部是无法访问的。

27. 正则表达式实例


var a = /123/;
var b = /123/; a == b; a === b; // A. true, true // B. true, false // C. false, false // D. other 

答案是C。每个字面的正则表达式都是一个单独的实例,即使它们的内容相同。

28. 数组也爱比大小


var a = [1, 2, 3];
var b = [1, 2, 3];
var c = [1, 2, 4];a == b;
a === b;
a > c;
a < c;// A. false, false, false, true
// B. false, false, false, false // C. true, true, false, true // D. other 

答案是A。数组也是对象,ES5规范指出如果两个对象进行相等比较,只有在它们指向同一个对象的情况下才会返回 true,其他情况都返回 false。而对象进行大小比较,会调用 toString 方法转成字符串进行比较,所以结果就变成了字符串 "1,2,3" 和 "1,2,4"按照字典序进行比较了(你若不信,可以重现两个变量的 toString 方法,进行测试)。

29. 原型把戏


var a = {};
var b = Object.prototype;[a.prototype === b, Object.getPrototypeOf(a) == b] // A. [false, true] // B. [true, true] // C. [false, false] // D. other 

答案是A。对象是没有 prototype 属性的,所以 a.prototype 是 undefined,但我们可以通过 Object.getPrototypeOf 方法来获取一个对象的原型。

30. 构造函数的函数


function f() {} var a = f.prototype; var b = Object.getPrototypeOf(f); a === b; // A. true // B. false // C. null // D. other 

答案是B。这个解释起来有点绕口,我们先来看另外一段代码:

function Person() {} var p = new Person(); var a = p.__proto__; var b = Object.getPrototypeOf(p); var c = Person.prototype; console.log(a === b, a === c, b === c); // true, true, true var d = Person.__proto__; var e = Object.getPrototypeOf(Person); var f = Function.prototype; console.log(d === e, d === f, e === f); // true, true, true 

首先你要明白,任何函数都是 Function 的实例,而p是函数 Person 的实例,Object.getPrototypeOf 会获取构造当前对象的原型。所以 Object.getPrototypeOf(p) === Person.prototype,而 Object.getPrototypeOf(Person) === Function.prototype,所以答案就很明显了。

我解释的不是很好,如果读者有更好的解释,欢迎评论。

31. 禁止修改函数名


function foo() {} var oldName = foo.name; foo.name = "bar"; [oldName, foo.name]; // A. error // B. ["", ""] // C. ["foo", "foo"] // D. ["foo", "bar"] 

答案是C。函数名是禁止修改的,规范写的很清楚,所以这里的修改无效。

参考资料:

  • MDN:Function.name

32. 替换陷阱


"1 2 3".replace(/\d/g, parseInt);// A. "1 2 3" // B. "0 1 2" // C. "NaN 2 3" // D. "1 NaN 3" 

答案是D。如果 replace 方法第二个参数是一个函数,则会在匹配的时候多次调用,第一个参数是匹配的字符串,第二个参数是匹配字符串的下标。所以变成了调用 parseInt(1, 0)parseInt(2, 2)parseInt(3, 4),结果你就懂了。

参考资料:

  • MDN:String.prototype.replace()

33. Function的名字


function f() {} var parent = Object.getPrototypeOf(f); console.log(f.name); console.log(parent.name); console.log(typeof eval(f.name)); console.log(typeof eval(parent.name)); // A. "f", "Empty", "function", "function" // B. "f", undefined, "function", error // C. "f", "Empty", "function", error // D. other 

答案是C。根据第30题的解释,我们知道代码中的 parent 实际上就是 Function.prototype,而它在控制台中输出为:

function () {[native code]
}

它的 name 属性是 "",所以你 eval("")是得不到任何东西的。

34. 正则测试陷阱


var lowerCaseOnly = /^[a-z]+$/;
[lowerCaseOnly.test(null), lowerCaseOnly.test()]// A. [true, false] // B. error // C. [true, true] // D. [false, true] 

答案是C。test 方法的参数如果不是字符串,会经过抽象 ToString操作强制转成字符串,因此实际上测试的是字符串 "null" 和 "undefined"

35. 逗号定义数组


[,,,].join(", ")// A. ", , , "
// B. "undefined, undefined, undefined, undefined"
// C. ", , "
// D. "" 

答案是C。JavaScript允许用逗号来定义数组,得到的数组是含有3个 undefined 值的数组。MDN关于 join 方法的描述:

所有的数组元素被转换成字符串,再用一个分隔符将这些字符串连接起来。如果元素是undefined 或者null, 则会转化成空字符串。

参考资料:

  • MDN:Array.prototype.join()

36. 保留字 class


var a = {class: "Animal", name: "Fido"}; console.log(a.class); // A. "Animal" // B. Object // C. an error // D. other 

答案是D。实际上真正的答案取决于浏览器。class 是保留字,但是在Chrome、Firefox和Opera中可以作为属性名称,在IE中是禁止的。另一方面,其实所有浏览器基本接受大部分的关键字(如:intprivatethrows等)作为变量名,而class是禁止的。

37. 无效日期


var a = new Date("epoch"); // A. Thu Jan 01 1970 01:00:00 GMT+0100(CET) // B. current time // C. error // D. other 

答案是D。实际结果是 Invalid Date,它实际上是一个Date对象,因为 a instance Date的结果是 true,但是它是无效的Date。Date对象内部是用一个数字来存储时间的,在这个例子中,这个数字是 NaN

38. 神鬼莫测的函数长度


var a = Function.length; var b = new Function().length; console.log(a === b); // A. true // B. false // C. error // D. other 

答案是B。实际上a的值是1,b的值是0。还是继续来看MDN文档关于 Function.length的描述吧!

Function构造器的属性:

Function 构造器本身也是个Function。他的 length 属性值为 1 。该属性 Writable: false, Enumerable: false, Configurable: true。

Function原型对象的属性:

Function原型对象的 length 属性值为 0 。

所以,在本例中,a代表的是 Function 构造器的 length 属性,而b代表的是 Function原型的 length 属性。

参考资料:

  • MDN:Function.length

39. Date的面具


var a = Date(0);
var b = new Date(0);
var c = new Date();
[a === b, b === c, a === c];// A. [true, true, true]
// B. [false, false, false] // C. [false, true, false] // D. [true, false, false] 

答案是B。先看MDN关于Date对象的注意点:

需要注意的是只能通过调用 Date 构造函数来实例化日期对象:以常规函数调用它(即不加 new 操作符)将会返回一个字符串,而不是一个日期对象。另外,不像其他JavaScript 类型,Date 对象没有字面量格式。

所以a是字符串,b和c是Date对象,并且b代表的是1970年那个初始化时间,而c代表的是当前时间。

参考资料:

  • MDN:Date

40. min与max共舞


var min = Math.min();
var max = Math.max(); console.log(min < max); // A. true // B. false // C. error // D. other 

答案是B。看MDN文档,对 Math.min的描述:

如果没有参数,结果为Infinity。

对 Math.max 的描述:

如果没有参数,结果为-Infinity。

参考资料:

  • MDN:Math.min
  • MDN:Math.max

41. 警惕全局匹配


function captureOne(re, str) { var match = re.exec(str); return match && match[1]; } var numRe = /num=(\d+)/ig, wordRe = /word=(\w+)/i, a1 = captureOne(numRe, "num=1"), a2 = captureOne(wordRe, "word=1"), a3 = captureOne(numRe, "NUM=1"), a4 = captureOne(wordRe, "WORD=1"); [a1 === a2, a3 === a4] // A. [true, true] // B. [false, false] // C. [true, false] // D. [false, true] 

答案是C。看MDN关于 exec 方法的描述:

当正则表达式使用 "g" 标志时,可以多次执行 exec 方法来查找同一个字符串中的成功匹配。当你这样做时,查找将从正则表达式的 lastIndex 属性指定的位置开始。

所以a3的值为 null

参考资料:

  • MDN:RegExp.prototype.exec()

42. 最熟悉的陌生人


var a = new Date("2014-03-19"); var b = new Date(2014, 03, 19); [a.getDay() == b.getDay(), a.getMonth() == b.getMonth()] // A. [true, true] // B. [true, false] // C. [false, true] // D. [false, false] 

答案是D。先看MDN关于Date的一个注意事项:

当Date作为构造函数调用并传入多个参数时,如果数值大于合理范围时(如月份为13或者分钟数为70),相邻的数值会被调整。比如 new Date(2013, 13, 1)等于new Date(2014, 1, 1),它们都表示日期2014-02-01(注意月份是从0开始的)。其他数值也是类似,new Date(2013, 2, 1, 0, 70)等于new Date(2013, 2, 1, 1, 10),都表示时间2013-03-01T01:10:00。

此外,getDay 返回指定日期对象的星期中的第几天(0~6),所以,你懂的。

参考资料:

  • MDN:Date

43. 匹配隐式转换


if("http://giftwrapped.com/picture.jpg".match(".gif")) {console.log("a gif file"); } else { console.log("not a gif file"); } // A. "a gif file" // B. "not a gif file" // C. error // D. other 

答案是A。看MDN对 match 方法的描述:

如果传入一个非正则表达式对象,则会隐式地使用 new RegExp(obj)
将其转换为正则表达式对象。

所以我们的字符串 ".gif" 会被转换成正则对象 /.gif/,会匹配到 "/gif"

参考资料:

  • MDN:String.prototype.match()

44. 重复声明变量


function foo(a) { var a; return a; } function bar(a) { var a = "bye"; return a; } [foo("hello"), bar("hello")] // A. ["hello", "hello"] // B. ["hello", "bye"] // C. ["bye", "bye"] // D. other 

答案是B。一个变量在同一作用域中已经声明过,会自动移除 var 声明,但是赋值操作依旧保留,结合前面提到的变量提升机制,你就明白了。

参考资料:

  • MDN:var

转载于:https://www.cnblogs.com/gluncle/p/10700331.html

相关文章:

  • margin-top的兼容问题
  • [Swift]LeetCode311. 稀疏矩阵相乘 $ Sparse Matrix Multiplication
  • 三、Oracle 查询+where条件
  • C#把汉字转换成16进制(HEX)并向串口发送数据
  • 强大的 actuator 服务监控与管理
  • 《CoderXiaoban团队》第一次作业:团队亮相
  • VS Code 快捷键设置
  • git配置ssh秘钥(公钥以及私钥)windows
  • [題解]luogu_P2055假期的宿舍(二分圖最大匹配)
  • xtrabackup 2.4.3 BUG
  • PCL Examples
  • CentOS hgfs中没有共享文件夹
  • LOJ 6409. 「ICPC World Finals 2018」熊猫保护区
  • Django+xadmin的安装与配置
  • 团队项目准备阶段
  • Google Kickstart Round.B C. Diverse Subarray
  • Hard To Get歌词分析
  • linux——文件操作
  • bzoj 1926: [Sdoi2010]粟粟的书架 (主席树+二分)
  • poj 3264 Balanced Lineup RMQ问题
  • kudu导入文件(基于impala)
  • JAVA基础—变量
  • Java知识系统回顾整理01基础01第一个程序05Eclipse中运行Java程序
  • openstack cinder api对应的命令行接口
  • geatpy - 遗传和进化算法相关算子的库函数(python)
  • 剑指offer-面试题37:序列化二叉树及二叉树的基本操作和测试
  • eclipse集成testng插件
  • yum 安装Mysql8.0
  • 2019大一下学期第九周总结
  • Vue 读取Excel数据
  • [Web 前端] 005 html 常用标签补充
  • antd的Tree控件实现点击展开功能
  • 05 数组
  • SLA服务可用性怎么达到?
  • 关于阿里巴巴发布普惠体的一些思考
  • 自动化测试基础篇--Selenium弹出框alert
  • Python常用模块实战之ATM和购物车系统再升级
  • Vue使用Axios实现http请求以及解决跨域问题
  • 2015 ACM/ICPC Asia Regional Changchun Online HDU - 5441 (离线+并查集)
  • 关于选择器注意的点
  • Hive学习笔记(二)——数据模型
  • Android的启动方式
  • chrome断点调试其他技巧
  • x86汇编语言复习笔记
  • Windows环境下安装Hadoop+Hive的使用案例
  • leetcode1037 有效的回旋镖(Java练习)
  • asBroadcastStream
  • 文件操作的初识
  • IDEA启动Tomcat时 , 报错提示:this web application instance has been stopped already
  • MySql事务及ACID实现的原理
  • springMVC简介
  • 无线网卡
  • 雷林鹏分享:jQuery EasyUI 菜单与按钮 - 创建链接按钮
  • SpringBoot学习笔记(15):动态数据源切换
  • 一起来学Spring Cloud | 第一章 :如何搭建一个多模块的springcloud项目
  • *P1108 低价购买 dp
  • 小程序的switch组件
  • Go 切片
  • 织梦 自动序号排序
  • 数组去重,简单有效
  • 用bootstrap模态框的时候,点击多少次按钮(确定、关闭),后台提交多少次Ajax,重复提交...
  • Glusterfs的安装及简单使用
  • [JZOJ4640] 【GDOI2017模拟7.15】妖怪
  • 基于python、jupyter-notebook 的金融领域用户交易行为分析
  • Java工程师学习指南
  • 服务器复制不进去文件等
  • Python---基础-运算符int和range函数
  • wx:if 与hidden
  • Java使用笔记之stream和sorted使用
  • mac上安装搭建Appium环境
  • django-rest-framework-jwt的使用
  • centos安装python3、redis和虚拟环境
  • 2019-05-22 SperScan扫描器;SperScan附属工具
  • Scrapy 中的 Request 对象和 Respionse 对象
  • OO第三单元总结——JML
  • 原生js添加鼠标事件的兼容性写法
  • 基础知识 +考试
  • luogu 1337
  • 第十三周学习进度
  • Jmeter在Windows上分布式压测遇到的坑
  • 碰运气解决LATEX中中文颜色深浅不一的问题
  • sqli-labs(19)
  • 01_Navicat的快捷键学习
  • 厉害了
  • 从零开始学MySQL(四)
  • 【整理操作】MQTT简单使用学习
  • json数据交互
  • sort(排序)
  • 洛谷 P1025 数的划分
  • SpringBoot源码篇:Spring5内置tomcat实现code-based的web.xml实现
  • Tensorflow从开始到放弃(技术篇)
  • Asp.Net Core 第03局:Startup
  • 关于http-server的备选方案-- browser-sync
  • 用户体验报告(Echo)
  • H5踩坑系列(一)
  • 并发编程理论
  • Biorhythms 中国剩余定理
  • day7-mysql引擎和索引
  • ASP.net发布项目引用了C++DLL后页面提示找不到指定模块的异常
  • C#基于RabbitMQ实现客户端之间消息通讯实战演练
  • Vue:router的beforeEach与afterEach钩子函数
  • String的拼接
  • JVM(二):垃圾回收
  • git commit或pull后恢复到原来版本
  • 【面试题总结】1、统计字符串中某个单词出现的次数(1-C++实现)
  • 虫师Selenium2+Python_11、自动化测试项目实战
  • Debian9安装QT5.12.3
  • Django2.2中Xadmin错误集
  • java面试题 springmvc 解决post get请求乱码问题
  • SpringMVC+Spring+Mybatis简单总结
  • 代码示例_mmap的实现
  • 第一次作业 三班20
  • 初学者——实用类
  • zabbix_nginx监控
  • 带着身体在香港肤浅行走(一)
  • arcgis api 3.X 几种查询方式比较
  • nodejs之querystring模块
  • 任务队列 与线程池
  • (好文章搬个砖)MySQL索引背后的数据结构及算法原理
  • Ubuntu U盘启动出现“Failed to load ldlinux.c32”解决
  • jmeter监控服务器CPU、网络、内存等信息
  • 前端知识点回顾——Javascript篇(三)
  • 编写高效优雅Java程序
  • Beta冲刺提交-星期五
  • rust-vmm 学习
  • Oracle物化视图的一般使用【转】
  • [转]用户空间和内核空间,进程上下文和中断上下文
  • 浅谈 Attention 机制的理解
  • 二、$CSS部分
  • jvm-内存结构--分类索引
  • micropython logging文档
  • CST,CET,UTC,GMT,DST,Unix时间戳几种常见时间概述与关系(转)
  • 浏览器,图片格式及特点
  • gamma原理分析
  • 课程设计第一次实验总结
  • 【leetcode_easy】590. N-ary Tree Postorder Traversal
  • Photoshop cc2015基础课笔记
  • json dumps dump区别
  • 关于人生的思考(暂时想不出更好的标题)
  • 简述prototype, _proto_, constructor三者的关系
  • 2019 支付宝App支付 --- PHP
  • 02-CSS基础与进阶-day11_2018-09-17-21-35-14
  • 数据库 -- 单表的数据查询
  • Spring Boot 集成undertow作为web容器分析
  • echarts常用术语
  • OpenCV2:幼儿园篇 第七章 界面事件
  • [HG]AK 题解
  • linux限制进程使用的cpu使用率
  • Python学习笔记25:接口类、抽象类和封装
  • ASP.NET Core 静态文件 - ASP.NET Core 基础教程 - 简单教程,简单编程
  • JS 使用RSA加密解密
  • laravel使用artisan报错SQLSTATE[42S02]: Base table or view not found: 1146
  • Linux开机启动流程详细步骤是什么?
  • ubuntu 本地源搭建
  • 快读代码level.2
  • springboot的Interceptor、Filter、Listener及注册
  • 201671030119 唐盖强 实验十四 课程学习总结
  • 查看Linux占用内存/CPU最多的进程
  • 查询被锁的表
  • 每日一码——链表基本操作
  • NLP之电影评分数据的情感分析
  • Appium+python自动化(十一)- 元素定位秘籍助你打通任督二脉 - 下卷(超详解)...
  • 剑指:合并两个排序的链表
  • SSM+Redis简介
  • 网卡中断及多队列
  • urllib 库的代替品 requests 的用法
  • Linux基础-15-samba服务
  • 数据结构与算法之PHP排序算法(桶排序)
  • Maven的Scored介绍
  • Docker 比较好的新入门教程
  • SpringBoot 的配置文件
  • [转载]云架构师的进阶之路
  • [转帖]CentOS 7安装并启动Google浏览器(★firecat亲测有效★)
  • Delphi 日期函数列表
  • 从客户端中检测到有潜在危险的 request.form值
  • code forces 1176 D. Recover it!
  • 我的kindle书单
  • 最近很痛苦
  • Mysql 设置问题,当使用一些开源的软件动态安装MYSQL数据库后,发现里面的中文全是乱码。...
  • 操作系统面试题(二)
  • 训练深度神经网络提示
  • 软件需求分析学习笔记
  • 基于Open XML 导出数据到Excel
  • mysql事物回滚
  • acm集训训练赛(二)D题【并查集】
  • ScrollView不设置contentSize属性依然也可以作为底层滚动View(使用masonry设置scrollView的contentSize)...
  • 深入JavaScript之获取cookie以及删除cookie
  • dva 使用问题
  • qdtuling.xyz 7.10
  • 小程序 长按复制文本
  • 开启Hive的本地模式
  • word中的总页数不包括封面、目录
  • 转 Git-fatal: unable to access 'xxx' : Could not resolve host: xxx
  • Java(面向对象OOP)
  • HZNU 2019 Summer training 7
  • C语言实现—学生成绩管理系统(Linux下运行)
  • 『提醒自己』-自身遗忘掉的知识1
  • 【ABAP系列】SAP DOI技术中I_OI_SPREADSHEET接口的使用
  • 08.存储Cinder→5.场景学习→01.LVM Volume
  • 日期转换成时间戳的问题
  • 杭电acm2030汉字统计
  • 算法-归并排序
  • 【HANA系列】SAP HANA中null变成问号的问题
  • 使用回溯算法结合递归树+备忘录解决01背包问题
  • string成员函数
  • 《Python编程从入门到实践》学习笔记7(第8章:函数)
  • 在linux使用git的操作,几个问题解决
  • python可迭代对象和迭代器的理解
  • win7 64位Apache http server+PHP配置
  • 面向对象编程时,十条原则:
  • Flask中间件
  • 解决物理机U盘安装Kali Linux2018.1,光驱无法加载问题
  • asp.net core 系列 5 MVC框架路由(上)
  • [浪风分享]推荐一些不错的计算机书籍
  • qt下载地址
  • 【计算机网络基础】URI、URN和URL的区别
  • 使用读写管道作为事件通知的C++实现
  • 关闭蜂鸣最简单的方法
  • WINDOWS NT/2000下如何屏蔽CTRL+ALT+DEL
  • Delphi的命令行编译命令
  • sql plus命令大全(2)
  • mysql 利用延迟关联优化查询(select * from your_table order by id desc limit 2000000,20)
  • python 简单的接口测试框架
  • c# access oledb helper class
  • Redis:四、jedis连接redis服务器
  • 【带着canvas去流浪(13)】用Three.js制作简易的MARVEL片头动画(下)
  • vuex的store机制1
  • putty详细使用说明
  • Python 文件读写小结
  • c#中跨线程调用windows窗体控件
  • python函数知识七 闭包、装饰器一(入门)、装饰器二(进阶)
  • 利用GridView显示主细表并添加打开、关闭功能
  • chrome保护眼睛设置【转】
  • python 身份证校验位计算
  • Bootstarp4 按钮
  • 一个对oi选手的感想
  • 十一、对结果集查询
  • 最长公共子序列和最长子字符串_python_算法与数据结构
  • 个图标当十个用—多功能系统级图标制作攻略
  • Git工作区域
  • 【ant项目构建学习点滴】--(3)打包及运行jar文件
  • windows全部启动项
  • scrollLeft/scrollTop,offsetLeft/offsetTop,clientLeft/clientTop
  • 大话设计模式笔记(十二)の抽象工厂模式
  • React-Native 在 mac 下 build 问题
  • BZOJ 1901 洛谷 P2617 ZOJ 2112 Dynamic Rankings
  • Python Re 模块超全解读
  • 线程安全的list之synchronizedList和CopyOnWriteArrayList
  • Xcode5 取消项目ARC,或者单个类ARC切换
  • jquery 实现图片上传,并在前端显示出来
  • gcc/g++/make/cmake/makefile/cmakelists的恩恩怨怨
  • [转]Linux平台下的service程序编写指南
  • ssh登录失败处理步骤
  • SQL Server SQL语句调优技巧
  • 創建ASP組件修改《SERV-U》FTP 的密碼
  • caffe 训练solver配置
  • 证书分类
  • 基于线段树的RMQ
  • Jquery、简单的下拉列表、网页左部导航菜单
  • OpenNebula概述
  • ASP.NET2.0缓存(Cache)技术
  • Linux如何在系统启动时自动加载模块 .
  • Y COMBINATOR的六大强悍女人-转自应用电台
  • 数据库系统load飙高问题解决思路(转)
  • ConcurrentHashMap(JDK1.8)的源码解析
  • 二、自定义控件之RadioButtonList
  • 背包理解专栏
  • F5 root密码恢复
  • Linux下在Android模拟器中使用SD 卡的操作步骤
  • 在这个竞争的社会
  • Luogu2481 SDOI2010 代码拍卖会 DP、组合
  • CF468C Hack It! 构造
  • vue移动端,点击微信授权登录
  • UOJ424 Count 生成函数、多项式求逆、矩阵快速幂
  • ArcEngine创建IFeature的三种方法
  • NGINX实现负载均衡,并利用PHP实现session入库
  • C# 线程thread
  • 本两个Build工作总结
  • 【转】Linux配置使用SSH Key登录并禁用root密码登录
  • ASP.NET 实现文件下载
  • CF891E Lust 生成函数
  • 《学习之道》第十七章保持平静
  • SPOJ Qtree系列
  • Silverlight中使用Timer的方法
  • ElasticSearch 安装与API的使用
  • Kubernetes kubeadm 安装记录
  • Web设计的速查卡
  • AGC037
  • 一文搞懂Spring Cloud Zuul
  • linux jdk1.8 32位下载永久地址,ubuntu,centos,java
  • C#作业总结(3)
  • Codeforces Round #580 (Div. 1)(CF1205)A-E
  • 一个循环递归遍历问题
  • Apache服务器+Tomcat服务器配置
  • 红帽学习笔记[RHCSA] 第一周
  • 红帽学习笔记[RHCSA] 第一课[Shell、基础知识]
  • 红帽学习笔记[RHCSA] 第二课[文件、目录、相关命令]
  • 红帽学习笔记[RHCSA] 第三课[输出重定向、Vi编辑器]
  • 红帽学习笔记[RHCSA] 第五课[用户、权限相关]
  • 红帽学习笔记[RHCSA] 第六课[进程、服务相关]
  • 红帽学习笔记[RHCSA] 第七课[网络配置相关]
  • c#textBox控件限制只允许输入数字及小数点
  • sql ,类型转换,日期截取格式
  • QQ、空间、新浪微博、微信分享
  • 九省联考2018
  • TypeError: this.getResolve is not a function
  • 库开发遇到问题总结
  • Qt qss问题总结
  • BZOJ5312 冒险 势能分析、线段树
  • LOJ2001 SDOI2017 树点涂色 LCT、线段树
  • BZOJ3514 / Codechef GERALD07 Chef and Graph Queries LCT、主席树
  • CF1109F Sasha and Algorithm of Silence's Sounds LCT、线段树
  • LOJ6031 「雅礼集训 2017 Day1」字符串 SAM、根号分治
  • LOJ2874 JOISC2014 历史研究 分块、莫队
  • LOJ6504 「雅礼集训 2018 Day5」Convex 凸包、莫队
  • BZOJ3145 [Feyat cup 1.5]Str 后缀树、启发式合并
  • php使用RabbitMQ
  • Qt qss 动态属性-不同条件不同显示
  • Codeforces Round #584 (Div. 1 + Div. 2)
  • LOJ2392 JOISC2017 烟花棒 二分、贪心
  • LOJ2336 JOI2017 绳 贪心、构造
  • LOJ2257 SNOI2017 遗失的答案 容斥、高维前缀和
  • 牛客挑战赛32
  • sql select 0 字段 某字段是不在指定的表
  • AGC038
  • vue echart甘特图
  • android webview设置和遇到的坑
  • CF573E Bear and Bowling 贪心、分块、凸包
  • Codeforces Round #588 (Div. 1)
  • CF1190E Tokitsukaze and Explosion 二分、贪心、倍增、ST表
  • java字符串类型和时间类型的转换
  • 富文本编译器
  • 制作二维码java
  • java连接数据库增删改查公共方法
  • java根据数据库自动生成代码
  • java使用jsp建立项目+视频
  • ajax简单做html查询删除(鲜花)
  • 在eclipse中配置Tomcat时,出现“Cannot create a server using the selected type”的错误。...
  • $.ajax方法success方法窗口弹不出
  • java反射 反射构造函数 报 wrong number of arguments 错误
  • com.microsoft.sqlserver.jdbc.SQLServerException: Socket closed 或者 该连接已关闭
  • 运行出现Server Tomcat v8.5 Server at localhost failed to start.和A child container failed during start...
  • 图标链接
  • html下载文件和上传文件(图片)(java后台(HttpServlet))打开保存路径和选择文件录取+(乱码UTF-8)+包...
  • 关于eclipse中引入项目报错或者没有JRE System Library问题(jre报错)或者jre1.7(8)改为jre1.8(7)等问题...
  • 使用cmd命令进行运行java(cmd命令输出Hello word)
  • java连接数据库(sqlserver和mysql)
  • intellij idea下载安装以及创建项目(输出Hello World)
  • 使用intellij idea创建JSP和引入Tomecat包
  • 使用git作为首页以及克隆上传创建下载删除
  • 简单的使用git克隆上传创建下载删除
  • git上传过滤忽略文件
  • Intellij idea创建(包、文件)javaWeb以及Servlet简单实现(Tomcat)
  • WebSocket消息推送(实现进行聊天)和WebSocket简介
  • viewport原理和使用和设置移动端自适应的方法(移动适应电脑)
  • emmet工具使用和技巧
  • 在idea配置jetty和创建(包、文件)javaWeb以及Servlet简单实现
  • Git免密码提交
  • @WebListener 注解方式实现监听(eclipse和idea)
  • Java中基本类型和引用类型(简单介绍)
  • 关于java中Exception异常
  • java中equals以及==的用法(简单介绍)
  • java中String、StringBuffer和StringBuilder的区别(简单介绍)
  • 简单介绍三层架构
  • Intellij IDEA 报错java.lang.NoClassDefFoundError
  • Html5简单描述(优点与缺点)
  • 原生ajax接收json字符串(简单介绍)
  • 简单使用Idea创建三层架构项目和数据库连接(使用原生ajax进行访问+ajax)
  • 原生ajax、XMLHttpRequest和FetchAPI简单描述
  • TCP/IP协议简单介绍
  • servlet三种方式实现servlet接口
  • java多线程(简单介绍)
  • 线程和进程有什么区别(简单介绍)
  • java随机数Reandom(简单介绍)
  • sqlite配置下载安装教程
  • serlvet配置xml和@WebServlet
  • idea原生ajax数据处理(增删改查)
  • heidsql(mysql)安装教程和mysql修改密码
  • 三层架构(命名规则)
  • jstl有什么优势(简单介绍)
  • jsp:include和%@include file=%的区别(简单了解)
  • html5中event获取data和class
  • SiteMesh配置下载使用(简单介绍)
  • FastJSON、Gson、Jackson(简单了解使用)
  • 手机商城项目(修饰技术)
  • idea连接sqlite
  • mysql时间日期函数
  • 商城管理系统(前台+后台+管理员+用户+html+jsp)
  • 远程桌面连接(连接服务器)报错Oracle修正
  • sqlite简单笔记
  • mysql安装下载
  • MariaDB修改端口号
  • mysql(安装、启动、删除)服务
  • sqlite迁移mysql(导入导出数据)
  • C#框架
  • 了解Mysql与MariaDb的关系
  • 关于数据库设计是否需要加入(建立)外键
  • SQL中char、varchar、nvarchar、text 的区别
  • mysql中set和enum使用(简单介绍)
  • Mysql常见的引擎
  • mysql常用命令添加外键主键约束存储过程索引
  • mysql关键字了解
  • 自增、主键的优缺点(数据库)
  • mysql数值函数
  • 使用jdk进行数据迁移(sqlite迁移mysql)
  • maven环境、本地仓储配置(下载安装)idea配置maven
  • maven环境、本地仓储配置(下载安装)
  • mysql中locate和substring函数使用
  • maven简单了解,没有Maven和使用Maven的区别
  • 在idea中配置jetty
  • idea中使用maven方式使用jetty+cmd中使用Jetty运行(maven)Web项目
  • 测试 jdbc 中连接关闭的时机
  • git中Bash基本操作命令
  • MySQL 存储过程参数IN OUT INOUT区别
  • MySQL中的if和case语句使用总结
  • mysql随机字符串函数
  • prepareStatament和Statement和callableStatement的区别
  • MySQL游标(cursor) 定义及使用
  • MySQL 中while loop repeat 的基本用法
  • 冒泡排序(一分钟懂)
  • 在IDEA中创建Maven项目和添加tomcat
  • maven没有servlet(创建servlet后报错)
  • 多模块添加依赖关系
  • Maven仓库分类
  • Git版本控制的原理
  • 整合ssm集成框架
  • 获取字符串中不重复的第一个字符
  • MyBatis简单了解
  • maven解析xml+测试test+注解
  • 注解@Component,@Controller,@Service,@Repository简单了解
  • MyBatis的优缺点以及特点
  • java的四个元注解 @Retention @Target @Document @Inherited
  • 手动创建maven项目+cmd+webapp+tomcat
  • 简单使用hibernate(idea中使用)
  • 简单使用mybatis(idea中使用)
  • Mybatis中的DataSource配置
  • MyBatis中解决字段名与实体类属性名不相同的冲突
  • Mybatis-generator自动生成
  • 简单介绍Spring是什么?
  • log4j日志框架
  • 搭建ssm中遇到的问题
  • 使用maven搭建ssm项目配置+tomact
  • idea中不重启服务器更改代码(使用jrebel)
  • Spring转换编码utf-8方式
  • @NotEmpty@NotNull和@NotBlank的区别
  • springmvc中校验框架(hibernate)
  • spring中使用i18n(国际化)
  • java获取文件后缀名(正则表达式)+文件名
  • ajax(form)图片上传(spring)
  • 物流管理系统(数据库+后台+springMVC+Mybatis+layui)(一)
  • springmvc时间(date)无法转入后台(@DateTimeFormat+@JsonFormat(GMT+8))
  • 宠物商城后台管理系统(springMVC+Mybatis+数据库)
  • html上传图片(进度条变化)、音乐
  • 使用canvas上传图片+上传进度
  • axios简单了解
  • 手机验证码免费10条\java、C#、html....
  • 简单了解ajax
  • 3大框架Struts、Hibernate、Spring简单了解
  • Spring mvc文件下载
  • HBuilder实现WiFi调试Android
  • 手动解除浏览器跨域限制
  • 解决adb devices无法连接夜神模拟器
  • vue案列
  • 使用vue自定义组件以及动态时间
  • 使用Vue做评论+localStorage存储(js模块化)
  • mui(手机)
  • 使用vue搭建项目(创建手脚架)
  • 学校管理系统C#(数据库、源码、演讲内容、ppt等)
  • 学校管理系统设计java(数据库、源码、演讲内容、ppt等)
  • 学校管理系统java(数据库、源码、演讲内容、ppt等)
  • SSM命名规范框架
  • java提取(获取)博客信息(内容)
  • java读取资源文件(Properties)
  • 使用过滤器进行跨域
  • spring 跨域 CORS (Cross Origin Resources Share) 跨域
  • spring异常+自定义以及使用
  • java中io创建文件和读取文件
  • Spring mvc实现ex导入导出
  • Spring时间(Date)类型转换+自定义
  • Spring自定义注解(验证身份证+性别+地区)
  • 身份证信息
  • idea中ssm自动配置
  • vue使用element案列
  • java三大特性(封装、继承、多态)
  • 使用cmd命令创建vue(ivieiw)项目
  • Java构造器(构造方法)与方法区别
  • 工厂模式(简单、普通、抽象)
  • js array
  • iview(DatePicker)时间转入后台少一天 解决方案
  • Spring 通过XML配置装配Bean
  • 两种方式(xml+代码)构建SqlSessionFactory+完整实现
  • 通过Field (反射)给私有字段赋值
  • Spring使用java代码配置Web.xml进行访问service
  • spring加载属性(properties)文件
  • ssm分页
  • 学校管理 学生会管理系统(SSM)vue+ssm+shiro
  • 使用Spring实现AOP(XML+注解)
  • spring AOP 代理(静态与动态+使用cglib实现)
  • 使用@AspectJ注解开发Spring AOP
  • 用Java+xml配置方式实现Spring数据事务(编程式事务)