jsuser吧 关注:2贴子:40
  • 8回复贴,共1

JavaScript和C/C++语言的相通之处

只看楼主收藏回复


本文假设读者熟悉C/C++语言, 如果你不熟悉, 那么你可以忽略C/C++部分的论述, 只看JavaScript的部分就可以了, 这篇文章是笔者学习JavaScript语言时候的一些知识点.
JavaScript给笔者的印象一直是面向对象, 一切皆是对象, 包括函数. 我们可以给方便的给对象赋一个函数值, 于是它就成为了一个函数, 可以被呼叫执行. 但是, 事实上, 函数不过是一个指针, JavaScript对象只不过能够接受一个函数指针, 这是C/C++语言也具有的特性. 一般来说, 函数在内存里面只有一个拷贝, 即使对JavaScript来说也是如此.
JavaScript一切都是对象, 事实上, 脚本语言的变量无类型是一种假象, C/C++也有variant类型, 可以接受各种类型的赋值, JavaScript只不过语言预置了这种类型, 这个类型可以保存各种不同类型的数据. 事实表明, 即使放弃C++自由的自定义类型方式, 我们仍然能够做同样的事情, JavaScript就是如此. 使用一个或几个预制的类型, 我们完全可以构造各种数据结构, 因为即使是C++的类定义, 最终的类型还是那几种预定义类型, 复杂类型是简单类型的组合而已. JavaScript把预制类型减少到很少, 字串,数字,函数,对象, 而且你一般没有必要就不用管它是哪种类型, 最重要的是, 你不用像C/C++那样因为不同类型的变量不能互相赋值而造成不方便, JavaScript能完成大多数类型的自动转换.
JavaScript的变量无需声明就可以使用, 但是其实这是个误解, 任何脚本语言的变量都是需要声明的, 与其说不用声明, 准确的说是利用赋值语句来声明一个变量. 而且很多时候, 我们确实需要声明一个变量, 但是留待后面再赋值, 所以没有声明关键字的脚本语言, 使用起来多少有些不便, 你可能不得不给一个变量赋一个不必要的值, 来声明它. 而既没声明, 也没赋值的变量, 如果直接使用, 都会提示未定义的语法错误.


IP属地:山东1楼2013-01-01 14:30回复
    重要提示: JavaScript使用var关键字声明一个变量, 局部如果使用了一个没有用var声明的变量, 它必须不能和全局变量同名, 否则它是那个全局变量而不是一个新的局部变量. 这很正常, C/C++也是这样, 但是要命的是JavaScript不需要变量声明, 程序员可能就是想要一个局部变量, 但是恰好和全局变量同名了. C/C++这种错误是不会出现的, 因为你使用一个局部变量必须先声明它. 当然了, 另一个规则, 导致程序员几乎必须给函数体内的变量加上var声明, 因为一个没有用var声明的变量, 即使它只在一个函数体内部出现过, 实际上它是一个全局变量, 在任何地方都可以访问. 如果你不希望函数内的变量被别处使用, 还是加上var关键字, 否则很多函数你不知道哪个变量可能名称相同, 而被认为是同一个变量, 在函数执行过程中, 产生各种奇怪的问题(调了个函数, 我这的某个变量怎么就变了? 答案是被调的函数内部可能有一个同名变量, 也没有用var声明).


    IP属地:山东2楼2013-01-01 14:30
    回复
      JavaScript是动态脚本语言, 这句话真正的意思是: JavaScript的变量只有执行到那里才会被创建, 这和C/C++的声明是有区别的, 除了new的对象之外, C/C++的声明过的变量对象都是开始就在内存中存在的, 包括局部变量, 当然它们可能没有被初始化, 只不过预留了内存空间, 局部变量甚至会在堆栈中共用内存空间. 也有例外, JavaScript的函数如果是用function funcname(){}这样的方式声明的, 那么在执行任何语句前, 这些函数对象已经被创建. 但是仅限这种方式, 如果用 funcname = function(){}这种方式定义一个函数, 则必须执行到这一句, 函数才会被创建.


      IP属地:山东3楼2013-01-01 14:31
      回复

        JavaScript有一个重要的基本类型Object, 它是所有对象类型的基础类型, 但一个值类型的变量不是一个Object . 给一个变量赋值可以使用如下的方式:
        //整数
        var i = 0;
        //字串
        var str = "abc";
        //数组
        var a = ["a","b","c"];
        //对象
        var obj = {};
        //给对象赋初始值
        var obj = {
        name:"myObject";
        func:function(){alert("func call");}
        };


        IP属地:山东4楼2013-01-01 14:31
        回复

          到现在, 你只能使用赋值语句, 即"="号初始化一个变量. 这没有什么问题, 但是某些时候有些不方便, C/C++的new关键字以及类的成员函数调用, 都提供了方便, 很多时候C可以模拟C++的类成员函数调用, 你只要在全局保存一个对象指针, 函数调用的时候, 给这个指针赋一个特定的值, 那么所有函数的调用也就是针对这对象的, 实际上C++也就是这么实现的, 只不过它把这些东西自动化了, C++使用ecx保存this指针, 相当于每个成员函数多了一个隐形的参数, 你只要写对象的成员函数调用, 对象指针就会自动传给那个函数.
          JavaScript也提供了这种便利, JavaScript的核心就是函数调用, 一切都是函数, 我们无需什么类. JavaScript也有this机制, 这个this是谁呢? 谁呼叫这个函数就是谁, 比如: obj.func(); func函数内部的this就是这个obj. 如果不是这么呼叫呢? 直接定义一个函数func然后呼叫它, 那么此时, func内部的this就是一个默认值, 在网页上它是全局的window对象; 在其它平台, 也会对应某个对象, 或者null. 也就是说, 一个函数如果不是写给某个对象的成员, 而是直接调用的, 就不要滥用this, 因为那个this是全局默认的一个对象, 如果你真要访问它也用不着使用this关键字. 总之函数内部的this是依赖谁调用的.
          JavaScript的new JavaScript一般使用这样的语句创建一个对象: var obj = new MyObject(); 这里MyObject是一个函数. 这个语句做两件事, 创建一个Object对象, 然后用这个对象调用MyObject函数, 也就是MyObject被呼叫的时候, 内部this访问到的是这个新建的对象, 并且函数执行完后, 返回这个新建的对象. 我们会发现, 即使不用new关键字, 如果我们定义MyObject函数在内部创建一个Object对象, 并且对它进行某些操作, 然后返回这个值, 这样:var obj = MyObject(); 效果是相同的. 系统提供的函数比如Array,Object都可以用new或者不用new来调用, 作用是相同的, 但是这仅限于系统函数, 我们自己定义的函数, 这两种方式的调用, 效果是不同的. 注意, 如果不打算用new来创建对象, 函数就应该自己创建它, 而且不用使用关键字this. 这两者函数的写法是不同的.


          IP属地:山东5楼2013-01-01 14:32
          回复

            因为我们可以任意的给 Object 对象添加属性, 实际上一个对象可以看成一个类. 只不过这个类的定义是动态的, 而不是像C++那样必须预定义再实例化. 其实C++也可以写一个类, 然后提供给这个类动态添加数据和函数(指针)的方法, 只不过访问方式不能用"."或"->", 而是某个函数. 还有一点, 我们希望创建的这个对象, 一次就初始化好, 而不是先创建Object, 再调用一个函数初始化它. new 关键字就能达到这个效果. 先定义一个函数, 把对象需要初始化的代码写在这个函数里(用this访问将来要创建的对象), 然后只要这样写: var obj = new MyObject(param...); 就完成了obj的创建和初始化, 已经非常"像"C++的类了.
            值类型和对象类型的区别 不仅仅是规定谁是值类型, 谁是对象类型, 值类型和对象类型有分类的标准. 我们可以认为 JavaScript 的每一个变量都是指针, 指向一个对象或者不指向任何对象(此时它的值为null), 事实是不是这样, 并不是非常的重要, 我们不去管底层如何实现, 我们只需要知道这种逻辑, 事实上, 你也可以把null变量视为指向一个null对象. 那么, 值类型的特点在于, 它指向的是一个常数对象. 什么是常数对象呢? 通俗的说, 常数对象是一种没有属性的对象. JavaScript 这门语言只提供改变一个对象的属性的操作, 如果一个对象没有属性, 我们就无法改变它的值. 注意, 这里说的是对象而不是变量, 变量仅仅是一个指针, 给它赋值, 就是让它指向一个新的对象. 而 Object 就是有属性的对象. 实际上, 我们有必要区分变量和变量指向的对象这种两种概念, 因为你不能给值类型的对象赋予或改变属性, 所以你唯一能做的就是引用这对象或者把变量重新指向一个对象, 这会造成值类型看起来就像自身拥有那个值一样. 字串是值类型的对象, 所以你永远无法更改一个字串, 你能做的就是创建新字串. 还有一点, 一个对象类型就意味着它没有值, 它的全部内容都在属性里. 但是 JavaScript 的内置对象还有一些额外的东西, 比如函数能够执行, 它必然有 Object 之外的内部结构, 但是这些额外的内容, 我们用属性是访问不到的. 除此之外, 记住, 变量都是相同的, 所谓的值类型变量和对象类型变量, 是指它们指向的对象是何种类型. 所以 JavaScript 从来也没有复制拷贝的概念, 除非显示的调用一个函数, 任何给变量赋值的操作都是简单的把变量指向一个新的对象(这里把置 null 值理解成指向 null 对象), 而没有其它更复杂的操作.


            IP属地:山东6楼2013-01-01 14:33
            回复

              记住: JavaScript 变量从来都不拥有任何值, 所有 JavaScript 变量都是一个简单的指针, 当我们使用 "=" 给一个变量赋值的时候, 是让这个变量指针指向一个新的对象, 而不是改变它原来指向对象的内容.
              还有继承 JavaScript是如何完成继承功能的呢? 所谓继承, 是一个写好的类(对JavaScript是一个函数), 我们希望再定义一个类, 这个类具有我们想继承的类的函数和数据成员. JavaScript为了满足这个需求, 引进了prototype属性,这个属性是函数对象特有的, Object对象没有, 即使添加了也没有意义. 虽然函数也是对象(Object), 但是函数确实有两个方面和Object不同, 一个是可以调用, 再一个是 new 操作的时候 函数的prototype 属性成为继承的参考属性. 每个函数都有默认的prototype属性, 但是初始值就是一个空的Object, prototype属性就是一个普通的对象, 凡是对象都可以设置为一个函数的prototype, 但是数值类型比如数字和字串不行, 因为数值类型只有值, 没有属性, 而 prototype 只关心它的属性, 不关心它的值, 值对于prototype没有任何意义. JavaScript对象的属性其实就是C++对象的成员, 使用函数和 new 创建一个JavaScript对象的时候, 这个函数的prototype的属性(成员), 自动成为新创建对象的属性. 比如下面的代码:


              IP属地:山东7楼2013-01-01 14:33
              回复

                function A(){
                this.Name = "A";
                this.Call = function(){
                alert("A Call");
                }
                }
                function B(){
                this.Value = 0;
                this.Func = function(){
                alert("B Func");
                }
                }
                B.prototype = new A();
                var b = new B();
                for(var i in b){
                alert(">"+i+":"+b[i]);
                }
                运行代码, 我们可以看到b有4个属性,Value,Func,Name,Call. 继承的本质是 b 自动添加 B.prototype 具有的属性, 这里添加属性仅仅是添加了变量, 也就是b内部有4个变量, 但是这些变量指向的对象不是新的, 要么是B函数添加给它的, 要么是 B.prototype 相同名称的属性指向的那个对象. 如果B函数创建的属性恰好和 B.prototype 的某个属性同名, 那么你通过 (b.属性名) 访问到的是B添加的属性. 当然, 这仅仅是看起来是这样, 事实上, 真正的操作, 是 b 对象仅仅保存了一个 B.prototype 的指针, 并没有创建那些属性, 也就是说, b "记得" B.prototype , 当你访问一个属性时, 如果b自身没有, 解释器就到它保存的这个指针指向的对象里去找, 找到后就认为 b "有"这个属性. 如果我们给 b 的这个属性赋值, 那么, 这个时候才会真的创建这个属性, 当然, 这种赋值仅仅是改变了 b 自身这个属性的指向, 而 B.prototype 里的属性指向和指向的对象都没有变化. 之所以说, b "记得" B.prototype 是因为, 即使 b 已经创建完成, 我们改变了 B.prototype ,仍然会"感知", 也就是 b 的那些继承属性, 如果没被重写的话, 也会跟着变化, 当然会这样.


                IP属地:山东8楼2013-01-01 14:34
                回复

                  原型链 实际上, 每个 Object 都保存着一个 prototype 指针, 但是这个指针我们访问不到, 是一个隐形属性. 既然这个指针指向一个 Object 对象, 那么那个 Object对象, 也会有一个自己的 prototype 指针, 直到最终, 这个指针为空. 事实上, 最原始的 new Object() 对象的这个 prototype 指针就是 null. 处于这个链上的任何一个prototype 指针具有的属性, 都可以作为 它上级对象的属性被读取, 但是如果写入的话, 会新建一个属性, 而不会改变原型上的属性. ( 在某些浏览器里, 这个隐藏的 prototype 属性是可以通过 __proto__ 属性名来访问)
                  (上面的提过 new Object() 的原型是 null , 这其实是我自己的想当然, 因为原型链必须终止于 null . 但是事实上, 使用 new 创建的任何对象的原型 __proto__ 并不是 null (还好这个变量能访问), 而是一个内部 Object , 之所以称为内部 Object, 是因为它是一个系统内置的 Object 对象, 全局唯一. 它唯一不同于用户自创建 Object 的地方就在于它的 __proto__ 是 null. 我们借助 __proto__ 可以访问到这个变量, 但是我们不能删除它, JavaScript 没有强制删除对象的功能, 但是我们能改变它, 比如给它添加属性, 结果就是使所 JavaScript 有对象在原型链的作用下, 都具备了这个属性, 这也是为什么某些浏览器不提供 __proto__ 属性访问的原因)


                  IP属地:山东9楼2013-01-01 14:34
                  回复