分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow
也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!
最近语言学习有些疯狂, 从Ruby到Lisp, 然后是C#, 既然已经疯狂了, 就顺面学习一下javascript吧. 对javascript的印象一直不佳, 从骂脏话最多的使用者, 到使用者平反的世界上最被误解的语言, 从所谓的让人抓狂的特性, 到世界上任何可以用javascript实现的东西, 最终都会被javascript实现, 并且, 这是最后一个实现. 出处太多, 不一一列举, 知者已知, 不知者也没有必要为了这些无聊的言论特意找出处了.
其实也不是完全没有用过javascript, 以前在开发一个Unity项目的时候用过一下Unity里面的javascript, 只不过那个javascript我甚至都只能称之为UnityScript. 太多太多自己实现的特性, 而又有些不够完整. 现在, 认识一下真正的javascript吧.
环境
Mac OS X 10.8.2, node v0.8.16
需要解释一下, node跟浏览器里嵌入的javascript不一样, 不具有类似confirm
和prompt
等接口, 我用console.log
来输出.
概要
JavaScript本身就是设计为一个前端语言, 据说设计只用了10天, 有些缺陷, 但是的确足够简单. 虽然JavaScript The Definitive Guide和大多数的语言书籍一样厚如砖头, 但是其实语言本身的介绍只有前面近200页, 这个厚度其实也就和R&D中描述的C语言差不多.
也就是因为设计比较简单, JavaScript也被一些人认为不算是现代语言, 不具有现代语言的一些特性.
语法细节
- 可选的语句结束符
;
, 这个很少见. 不过一般的规范都推荐不要真的省. - 支持自增
++
,自减符号--
, 相对Ruby, Python来说, 这个要更习惯. - switch和传统的C语言语法类似, 但是可以支持字符串的case.
- 支持NaN, null, undefined这三种表示类似无意义的量的方式, 有的时候这是混乱的根源. 也许还要再加上Infinity.
- 与大部分语言一样, javascript也分为原生类型和引用类型, 其中原生类型在拷贝, 参数传递和比较时时通过值的方式, 而引用类型都是通过引用的方式.
- 字符串为不可变类型, 任何的改变处理都是生成新字符串. 比较时为值比较. 字符串的值比较我个人认为时更加自然的做法, 比Java那种变态的方式要自然的多. 你几乎要反复的告诉每一个新来的程序员, 字符串的值比较在java中要使用
equals
函数. - 动态类型语言, 变量通过
var
定义. - 支持用label方式的break和continue, 用于在多层循环中直接对外层循环进行break和continue.
- 完整并且传统的try, catch, finally异常机制. 除了C++没有finally不够完整以外, 几乎所有现在语言的异常都是这么设计的了.
字符串
javascript虽然说语法是类C的, 但是起点是Java, 所以尽管设计的面向对象系统虽然不是传统的模版式的, 但是javascript中的字符串都是对象.
"hello, world".length// out: 12
上述的代码在现在已经不稀奇了, 但是相对C++来说还是更先进的.(可见C++多落后了)
var str = "hello" + "," + "world!";console.log(str);// out: hello,world!
字符串支持+
操作符作为字符串连接.
javascript有个奇怪的地方是字符串和数字同时使用时:
console.log("3" + 4 + 5);// out: 345console.log(4 + 5 + "3");// out: 93
也就是说, 相对一些语言(比如php)会自动的将字符串转为数字来说, javascript是倾向于将数字转为字符串的. 其实因为这种用法过于灵活, 即使是Ruby和Python这样以灵活著称的语言都是不允许这样的自动类型转换的.
更诡异的还不只这些, 对于加法来说是如此, 对于乘法来说又是另外一回事:
console.log("3" * 4);// out: 12console.log("3" * "4");// out: 12
在乘法运算中, 因为javascript的字符串并没有像Ruby, Python一样对乘法的运算做出特殊解释(字符串的乘法表示重复), 所以默认会将字符串转为整数进行运算, 更诡异的是, 就算是两个字符串, 同样也会不报错的进行整数转换并且运算.
函数
函数在javascript中是第一类值, 同时还支持闭包. 这是javascript构成对象的基础.
function add(x, y) { return x + y;}var sub = function(x, y) { return x - y;}add(2, 3);sub(5, 3);// out: 5// out: 2
有上述两种函数构造形式, 在调用时没有区别. 其中第一种方法和传统的函数定义方式一样, 而第二种实际上就是匿名函数的定义方式了. 只不过因为javascript中函数是第一类值, 所以可以很方便的赋值.
匿名函数
匿名函数也被称为lambda, 是个很方便和有用的特性, 加上对闭包的支持, 以此衍生了很多特性. 也因此成就了javascript类函数语言的特性.
var caller = function(fun, leftParam, rightParam) { fun(leftParam, rightParam);}caller(function(a, b) { console.log(a+b); }, 10, 20);// out: 30
如上例所示, 匿名函数很重要的一个应用就是用于很方便的构建高阶函数. 也许上例有些太生造, 最常用的一个特性可能就是排序了, 因为排序的规则可能很多, 一般排序函数都允许再传入一个函数作为参数, 来指定排序的规则. 比如再javascript中, 普通的排序函数有些奇怪, 默认是按照字符串排序的. 见下例:
a = [1, 3, 2, 10, 20];console.log(a.sort());// out: [ 1, 10, 2, 20, 3 ]
这在大部分时候估计都不是我们要的做法, 默认这样子我是第一次看见, 这就像字符串和整数想加最后变成字符串一样诡异, 也许javascript本身设计的时候是作为前端检验表单啥为主的语言, 所以对字符串这么偏爱吧. 幸运的是, sort
函数还是可以传入一个函数作为排序规则的. 见下例:
a = [1, 3, 2, 10, 20];console.log( a.sort( function(a, b) { return a - b; } ) );// out: [ 1, 2, 3, 10, 20 ]
因为匿名函数和递归在javascript中使用的都比一般语言要多, 所以提供了arguments.callee
用于表示当前调用的函数, 以方便匿名函数的递归调用, 事实上, 相对一般用函数名的递归调用方式, 这种方式要更加符合DRY(Dont Repeat Yourself)原则, 因为当函数名更改的时候, 不用再更改递归调用的函数名了.
var factorial = function(n) { if (n <= 1) { return 1; } else { return n * arguments.callee(n - 1); }}factorial(4);// out: 24
更有意思的是, arguments.callee
在javascript的严格模式中是禁止的, 简单的说就是这种调用方法是官方不推荐使用的错误用法, 在将来甚至有可能废除, mozilla的解释是这种更DRY的用例本身很”weak”, 但是却阻止了inline优化的进行, 因为这种方式是通过引用un-inlined函数实现的, 也只有函数un-inlined时, arguments.callee
才可以引用到.
事实上, 我觉得这简直是因噎废食的做法, 因为现在虽然是这样实现的, 但是完全可以通过更好的语法分析, 然后进行编译器的优化, 而不是因此废弃这样有用的语法. 这种用法绝对不像是官方说的那么”weak”, 要知道, DRY几乎是软件设计领域头等重要的原则.
闭包
一个闭包就是一个函数和被创建的函数中的范围对象的组合. 因为闭包的强大特性和带来的方便, 很多传统的语言都逐渐了加入了对其的支持, 很多时候, 甚至被视为一个语言是否还算是跟上时代的标志.
function makeIncrementor(base) { var count = base; return function(num) { count += num; return count; }}obj1 = makeIncrementor(10);obj2 = makeIncrementor(20);obj1(1);// out: 11obj1(1);// out: 12obj2(2);// out: 22obj2(2);// out: 24
上面的例子较好的展示了闭包的特性, 可以获得上层函数的参数和变量, 并且各自互相独立, 因为闭包对局部状态的保存, 很多时候能当作一个对象来使用.
灵活的参数调用
function add(x, y) { return x + y;}add(2, 3, 4);add();add(2);// out: 5// out: NaN// out: NaN
上述代码在调用时不会发生错误, 而是直接把后面的参数抛弃掉.
甚至于, 后面的两个参数不够的函数调用, 会返回NaN
, 也不会发生错误.
本质上是因为一旦函数调用参数不够时, 后面的参数都会被置为undefined
. 所以虽然javascript不支持默认参数, 但是可以模拟出来.
function mul(x, y) { if (y === undefined) { return x * 10; } return x * y;}mul(10);// out: 100
更灵活的语法是可以通过arguments
变量来获取参数, 这样可以支持任意数量的函数参数.
function add() { var sum = 0; for (var i = 0, j = arguments.length; i < j; i++) { sum += arguments[给我老师的人工智能教程打call!http://blog.csdn.net/jiangjunshow
![]()