Javascript是一种脚本语言,从出生就被唾弃,一开始人们使用它只是为了解决诸如页面数据校验之类的问题。它基于prototype的面向对象实现一度被认为很丑很难用,甚至很多身处一线Web开发者都不是特别重视,没有给予足够的时间及精力去深入学习它。不过随着NodeJS的兴起、开源社区的支持、面向个人用户的互联网产品的短平快开发特点,以及Web2.0对用户体验的重视下,Javascript慢慢转正,在2014年4月的语言排行榜中上升到第9,算是交了一份不错的答案了。Javascript虽有不合理之处,但语言的设计整体上算是非常有趣,如果有兴趣,值得花多点时间来慢慢感受下这一门动态、弱类型、解释性语言。

Prototype-Based

  Javascript(以下简称JS)是一门基于对象的语言,与多数OO不同,在JS中你不能,也不需要定义一个类,JS的基于对象特性依赖于Prototype-based(另一种编程范型是class-based)。Prototype-based认为,我们不需要从类型中一次次制造出相似的实例对象,我们需要的是从一个对象中复制出另一个对象。每个对象都有原型,对象从原型处克隆出属性、方法,然后扩展自己的属性、方法。假设有对象a,对象b的prototype链接到对象a中,那么对象a的所有属性,以及属性的动态变化都会影响到b(注意是影响,不是赋值)。我们来看看Javascript中的例子。

  相信不需要过多解释,大家就能明白代码包含的意思(先忽略第三行吧,这是一种定义类的方式),注意,在给a.name 赋值后,b.name 确实是同时被修改了,但另一种情况,先给子对象b的属性赋值后,再修改a对象对应的属性(即本例中的age),b是不会受影响的。这种机制的原理与Javascript的原型链规则有关,以后我们再慢慢聊聊。

First-class Function

  JS中,函数是一等对象。一等对象是一个编程语言术语,指语言中可被当做参数、返回值进行传递,并可给变量赋值的对象,比如C#中的对象、基本类型(可能有人会觉得C#中的函数可以利用委托进行传递,所以它是一级对象,实际上在C#的IL层面,委托也是一种对象,也是Class的实例)。JS中函数是一等对象(这个概念很重要),所以你可以对它做能对其他对象做的任何事情,例如:

  你可以把函数赋值给变量,可以正常调用它,可以调用它的属性(上例中的length),可以给它添加属性(上例中的age),可以把它赋给另一个变量(上例中的func2),可以给承载函数的变量赋另一个类型的值,与其他对象别无二致,遵循同一套语法,同一套操作。如果继续上面的例子就可以看到下图,证明函数是 Function 的一个实例。

Dynamic Programming Language

  动态是JS里面一个有人欢喜有人忧的特点。语言的动静特性,包含两个含义,一是结构层面的,即语言中的对象结构是否允许动态变化(简单的理解就是对象属性的动态添加删除,内存层面则是存储在堆中的对象可以动态扩展内存);二是功能层面的,即语言中的功能(一般表现为函数)是否允许动态变化。JS是动态语言,所以在实际运行中你并不知道某对象的合法性,在大型项目开发中容易出现问题,这也是所有动态语言的通病。相对而言,静态语言则特别严谨,可以在编译阶段发现潜在的许多问题,但实际用起来又比较繁琐,诸多限制。不同技术有不同的适用场景,会在一些环境下解决一些问题,然后在另外一些环境中带来一些问题,单纯讨论动静特性的好坏并无意义(不过,从个人学习角度出发,如果你想在技术这条路走久走远,建议多学习多使用不同编程语言、编程范型,可以看看《七周七语言》这本书。另外,动态语言应该是下一个技术方向,连C#都在支持dynamic关键字了)。

  我们不妨试着从另外一个角度来看待JS的动态这个特性。就现在而言,世界上有很多很多网站(这些域名连接在一起可能可以围绕世界好几圈,12px的字体),这些网站承载着200多个国家几百甚至几千万个公司、个人,以多种方式(博客、论坛、电商、微博。。。)发布的海量信息(信息大爆炸,Big bang!),这种情况下,你觉得Web技术最需要什么?没错,是包容性(从XHTML到HTML5的变化就可见端倪了)。HTML就是一个极具包容性、扩展性、简单的标准,而主流Browser实现的HTML解析器更是进一步提高了这种包容性。对应这一点,如果JS是一种静态语言?如果JS是C#、JAVA(绝对没有嘲笑的意思),你能想像你需要设计一颗继承树来实现一些功能扩展(不是说这种方式不行,Ext JS就是这样设计的,只是用起来不是很灵活)?你能想象你需要用大量的反射、元编程方法来实现一些类型变化?不是说这种严谨的方式不行,但严谨意味着严格,严格意味着轻易就拒绝你,所以严谨的技术(或者其他事物)不容易大范围铺开,不容易吸引大量的人去尝试,不适合Web这种开放的环境。言归正传,我们还是用一个例子来说明动态性吧:

  上面的例子很简单,先定义一个对象,然后给它一个本来不存在的属性赋值(改变结构),这样子的操作是合法的,甚至是改变函数对象的结构(上面已经说了,函数是一等对象,可以执行任何语法运行的操作)。上面的例子是针对“对象”,即引用类型的,如果是值类型呢?

  诶,undefined?哪是不是JS针对值类型不存在动态性呢?不是的,要理解这一点首先要理解“包装类”的概念,上面已经提到,JS宣传处处皆对象,实际上值类型是通过类型转换实现的,每一个基本类型(string,number,boolean...)都会有对应同名的引用类型(String,Number,Boolean...),基本类型本身是一个存储在栈结构中的值,没有方法表,也就没有方法可言,但在对它进行方法调用时,解释器会创建一个相对应引用对象(相当于对原来的值进行包装),调用方法的操作实际是通过该对象进行的。上例中改变对象结构操作的是针对这个转换后的对象进行的(所以不会报错),但b仍然是原来那个值为“1”的值对象,所以没有发生变化(b.age 是 undefined 的)。需要注意的是,上述的结构变化是没有意义的,因为没有办法引用到那个解释器创建的“包装”对象。如果实在想改变值类型的结构,也是办法的:

  上图就是通过改变Number类型(实际上是一个函数)的原型实现的结构扩展,这是一种迂回但有效的做法,不过实际开发中一般不会贸贸然修改解释器中的原生对象。

Interpreted Language

  相信大家都知道,至少听过解释性语言。语言是表达思维的工具,需要一个特定的上下文环境才能被理解,编程语言能被计算机理解、执行的上下文环境一般指语言对应的编译器、解释器,可以把它简单理解为一个翻译者。翻译者有两大特点,一是大部分语言一般都有多个翻译者,例如JS的V8、Jhakar、JaegerMonkey,C++的GCC、MSC、TC等等,所以不要把语言与翻译者搅混了,语言是相对独立的;而是翻译者有多种工作方式,可以从头到尾翻译一篇文章,也可以一边听一边翻译,这对应着编译器、解释器的工作机制;三是无论怎样的翻译者,受限于计算机本身的体系结构,它们最终的目标都是机器码(有很多是翻译成中间代码,但这些中间代码最终还是需要被变成二进制才有意义)。

  编译性是指语言编写的代码,在实际运行前,会通过编译器一次性,将所有代码翻译成机器语言,即二进制代码。编译语言的优点有很多很多,比如性能,程序运行的是最直接的二进制代码,不用其它操作;比如代码足够安全,因为二进制(即使是汇编)代码很难看懂,反编译起来一般都比较麻烦。很多高大全的语言,像Pascal、C、C++、C#(会先编译成IL,保证多语言兼容性;运行时编译成二进制代码,注意,是一次性的)等。编译性可以给语言带来许多好处,比如静态类型检查,非常适合商业开发,曾经(现在也是)是许多公司追逐的宠儿(从这个角度也可以看出,封闭、多人协作、软件工程化曾是软件开发的主流思想)。

  解释性则指语言编写的代码会以某种形式(非机器码)存在,在实际运行时,由解释器对逐行代码进行解释、翻译成功二进制代码,交由机器执行。注意,是逐行解释,也就意味在运行时,计算机除了要执行你的代码,还要执行解释器。解释性的优点,主要体现在它移植性比较强(在现今WEB时代,这可是很吃香的),虽然这是通过牺牲效率为代价的(计算机硬件技术的迅速发展,已经足以让这成为不那么重要的因素了)。许多脚本语言,如PHP、Javascript、Basic、Ruby都是解释性的(脚本语言设计的初衷就是简化开发过程编写、编译、链接、运行这个繁琐步骤)。解释性可以为语言带来动态、易扩展等特点,你甚至可以对语言本身进行编程(Ruby的元编程就是一个活生生的例子了),加上它表现良好的移植性,在这个讲究快速迭代的互联网时代,越来越受关注(甚至有一些厂商不惜花重本为原来的编译器带来一些解释性质),可以认为,在不久的将来解释性语言极有可能成为主流语言。

  语言的解释性、编译性并不是泾渭分明的,比如Java,很多人一直闹不清它的性质,其实简单的说,它是先编译(将代码一次性编译成字节码,在这个过程中执行一些静态类型、安全性检查),后解释(JVM加载字节码,并逐行解释成机器码并运行),所以它宣称拥有编译语言的安全性,也具有解释语言的可移植性(就我所知,基本扯淡)。另外一个异类是C#(其他基于.NET平台的语言也是),它也分两个步骤,即先编译(将代码一次性编译成IL,中间语言,用于多语言间的适配,毕竟.NET的初衷就是支持多语言协作),再编译(在实际运行时,将IL编译成机器码,当然,你也可以选择在你的用户使用前执行这个步骤)。顺便说一句,博客园里面的月经贴最近好像有点多,其实学什么都一样,重要的不是工具,而是使用工具的人,C#这么语言真挺好的,很适合拿来学面向对象思想,我用着就挺爽的。

后言

  说了这么多,抛开技术来说说其他的吧。这是我开通博客的处女贴,也是我毕业工作后的第一篇博客,文章内容准备了很久,但行文却是一气呵成,所以可能有很多语法问题,有很多错别字,看在这份上大家拍轻点啊;如果大家觉得是干货,也别客气啊,哈哈。我打算写针对Javascript(在这个基础上,尽力延伸其他内容)写一个系列,内容主要围绕JS及JS在浏览器上的实现,上面简略提到的很多内容都会在适当的时候详细的聊聊,行文风格会跟这篇差不多(如果大家觉得不好,请在留言说明,我一定改),质量则希望是越来越好,总之,有任何问题都请留言让我知道啊。

浅析Javascript的更多相关文章

  1. 浅析 JavaScript 中的 函数 currying 柯里化

    原文:浅析 JavaScript 中的 函数 currying 柯里化 何为Curry化/柯里化? curry化来源与数学家 Haskell Curry的名字 (编程语言 Haskell也是以他的名字 ...

  2. 浅析 JavaScript 中的 函数 uncurrying 反柯里化

    柯里化 柯里化又称部分求值,其含义是给函数分步传递参数,每次传递参数后部分应用参数,并返回一个更具体的函数接受剩下的参数,这中间可嵌套多层这样的接受部分参数函数,直至返回最后结果. 因此柯里化的过程是 ...

  3. 浅析JavaScript访问对象属性和方法及区别

    属性是一个变量,用来表示一个对象的特征,如颜色.大小.重量等:方法是一个函数,用来表示对象的操作,如奔跑.呼吸.跳跃等. 在JavaScript中通常使用”."运算符来存取对象的属性的值.或 ...

  4. 浅析JavaScript事件流——冒泡

    一.什么是事件冒泡流 我们知道事件流指的是从页面中接受事件的顺序. 为了形象理解事件冒泡,可以想象三军主将诸葛亮,在帐内运筹帷幄,眼观六路耳听八方,这时候前方的战事情况就需要靠传令兵来传达,当第一位传 ...

  5. 【JavaScript】浅析javaScript和HTML与unicode字符集的关系

    目录结构: // contents structure [-] javaScript和HTML的字符集 javaScript和HTML如何表现unicode字符集 参考文章 javaScript和HT ...

  6. 浅析JavaScript之Function类型

    JavaScript中的函数实际上是对象,每个函数都是Function类型的实例,而且都与其他引用类型一样具有属性和方法.由于函数是对象,因此函数名实际上只是指向函数对象的指针,保存函数在堆内存中的地 ...

  7. 浅析JavaScript引用类型之--Object、Array

    1.Object类型 对象是某个特定引用类型的实例,新对象有两种创建方式: i.使用new操作符调用构造函数来创建. var person = new Object(); person.name = ...

  8. 浅析 JavaScript 中的闭包(Closures)

    a { text-decoration: none; color: #4094c7 } h4,h5 { margin: 0; font-weight: 700; color: inherit; lin ...

  9. 浅析Javascript原型继承(转)

    引自: http://blog.csdn.net/kittyjie/article/details/4380918 原作者解释的浅显易懂,非常不错的JavaScript prototype总结 JS没 ...

随机推荐

  1. PL/SQL之--包

    一.包 包是一组相关过程.函数.常量.变量.游标.异常等PL/SQL程序设计元素的组合.它类似于C++和Java中的类,其中变量相当于类中的成员变量,过程和函数相当于类中的方法.通过使用包,可以使开发 ...

  2. Redis高级应用

    上一篇博文讲述了Redis的一些常用命令,可以对数据库及数据库服务器进行操作,本篇将讲述Redis的高级应用及配置 安全性 设置密码:修改redis.conf中的requirepass,在其后面添加密 ...

  3. 通用cube refresh方案

    通用cube refresh c# script 解决方法: 需要设置的变量如下: User::varcubename,User::varolapconnstr,User::varolapdbname ...

  4. [转]Oracle分页之二:自定义web分页控件的封装

    本文转自:http://www.cnblogs.com/scy251147/archive/2011/04/16/2018326.html 上节中,讲述的就是Oracle存储过程分页的使用方式,但是如 ...

  5. [原创]cin、cin.get()、cin.getline()、getline()、gets()、getchar()的区别

    这几个输入函数经常搞不清具体特点和用法,这里稍作总结 一.cin>> 1.最基本用法,输入一个变量值 2.输入字符串,遇“空格”.“TAB”.“回车”结束,比如输入“hello world ...

  6. docker containerd shim分析

    // containerd-shim is a small shim that sits in front of a runtime implementation that allows it to ...

  7. HttpClient如何解决302重定向问题

    最近的接口测试,发现接口地址报302错误,通过上网搜索,发现问题所在,解决办法是需要请求重定向后的URI. package com.btv; import org.apache.http.Header ...

  8. hdu 5773 The All-purpose Zero 最长上升子序列+树状数组

    题目链接:hdu 5773 The All-purpose Zero 官方题解:0可以转化成任意整数,包括负数,显然求LIS时尽量把0都放进去必定是正确的. 因此我们可以把0拿出来,对剩下的做O(nl ...

  9. [转载]ExtJs4 笔记(10) Ext.tab.Panel 选项卡

    作者:李盼(Lipan)出处:[Lipan] (http://www.cnblogs.com/lipan/)版权声明:本文的版权归作者与博客园共有.转载时须注明本文的详细链接,否则作者将保留追究其法律 ...

  10. Redis 一二事 - 在spring中使用jedis 连接调试单机redis以及集群redis

    Redis真是好,其中的键值用起来真心强大啊有木有, 之前的文章讲过搭建了redis集群 那么咋们该如何调用单机版的redis以及集群版的redis来使用缓存服务呢? 先讲讲单机版的,单机版redis ...