Javascript本质第一篇:核心概念

 

很多人在使用Javascript之前都至少使用过C++、C#或Java,面向对象的编程思想已经根深蒂固,恰好Javascript在语法上借鉴了Java,虽然方便了Javascript的入门,但要深入理解Javascript的时候,却带来了误导。作者在学习Javascript的时候曾陷入了这个误区,希望通过这篇文章让新学者避免走这个弯路,迅速正确地掌握Javascript。

1. 要点

在面对Javascript时,要牢记以下几点:

1.1 Javascript不是面向对象的编程语言

如果非要把Javascript归到面向什么的话,Javascript准确的说是面向原型的编程语言,从self编程语言发展而来,除了语法上借鉴Java,和Java什么关系都没有,本质上也不同。简单说,Javascript里面没有类,全是对象。在使用Javascript的时候,应该时刻提醒自己:Javascript不是C++、C#或Java。

1.2 Javascript是解释执行的语言

虽然这很显而易见,但如果不时刻牢记这一点,而把Javascript和编译型语言的运行方式混淆的话,也非常不利于理解Javascript。比如,如果你用C++、C#或Java中的局部变量的思想去理解Javascript函数中通过var定义的变量的话,就会有麻烦。

2. Javascript中的对象和类型

Javascript有Undefined,  Null,  Boolean,  Number, String, Object和Function这几种类型,但本质上Javascript中所有类型的对象都只是对象,都有共同的原型:一个内建的Object对象。只不过Javascript运行时对Undefined,  Null,  Boolean,  Number, and  String这五种基础类型的对象以及Function类型的函数对象以特定的方式解释和处理。

Javascript中的对象是键值对的集合,键的类型是字符串,值可以是任意对象。可以通过new 函数()来创建新对象,也可以通过{}语法来创建新对象。

函数也是对象,是一种包含可运行的代码的特殊对象,并且代码能够以函数调用的形式被执行。函数对象能通过function关键字定义或通过new关键字使用Function构造函数来创建。

下面的示例创建了一个函数对象foo,并为它设置三个属性:一个数字,一个字符串,一个函数。从示例可以看到函数对象除了能被调用以外,其他行为和普通对象无异。

函数可以作为构造函数通过new关键字来创建对象,在Javascript中,通常将构造函数称为对象的类型。比如obj = new Fun(),那么称obj的类型为Fun。

比如前面提到的函数对象创建的方法之一:fun = new Function("参数", "函数体"),Function本身是一个Javascript内建的函数。同理Boolean,  Number, String, Object, Array, Date等都是内建的函数。没有内建的Undefined和Null函数,Undefined和Null类型都只有内建的唯一的对象,分别是undefined和null。

3. 执行上下文

Javascript中每一行代码的执行都是在当前执行上下文中完成。

执行上下文的层级关系是由代码的定义结构(也就是源代码字面结构)决定的,与运行时函数的调用栈结构无关。

执行上下文由一个上下文变量容器和一个this绑定构成。上下文变量容器中包含当前执行上下文中定义的变量。this绑定表示当前执行上下文中this关键字指向的对象。

简单来说,每次函数调用都进入一个新的执行上下文,同一函数递的归调用也会进入新的执行上下文。

每一个执行上下文都包含对创建它的上一级执行上下文的引用,因此所有执行上下文组成一棵树。

在浏览器中,根执行上下文(即不在任何函数中运行的代码)中没有上下文变量容器,this绑定到全局对象window。因此在根执行上下文中定义变量时,使用var、不使用var和使用this.定义的变量都是全局对象window的属性,因为没有上下文变量容器。通过这一规定,Javascript运行过程中的所有执行上下文都能指向全局对象,形成一棵完整的树。

在除了根执行上下文之外的其它执行上下文中,变量定义的规则如下:

var i = 1 变量i位于当前执行上下文的上下文变量容器中 
j = 2 变量j是全局对象的属性
this.k = 3 变量k是当前执行上下文的this对象的属性

在表达式中使用一个变量时,Javascript将首先从当前执行上下文的上下文变量容器中查找该变量,如果不存在,则在上级执行上下文的上下文变量容器中查找,以此类推,直到根执行上下文,在根执行上下文中,将在全局对象中查找是否有同名的属性。示例如下:

当前执行上下文的this绑定默认继承自上级执行上下文,如果当前运行代码所在的函数是通过obj.currentfun()的方式调用,也就是obj对象的方法,那么this将被绑定到obj。在使用时,this关键字必须明确写出,因为this在逻辑上是与执行上下文有关的,与当前函数所属的对象没有直接因果关系。

垃圾回收机制:如果当前执行上下文及其中的代码已经执行完成,且其所有下级执行上下文已经释放,且没有来自当前执行上下文之外的对当前执行上下文的上下文变量容器中的变量的引用,那么当前执行上下文被释放,当前执行上下文的上下文变量容器中的变量指向的对象和this指向的对象的引用值减一(各引擎具体实现不同,这里描述的是原理)。

这也就是Javascript中常用到的闭包模式的原理。如果从传统面向对象语言的角度去看,闭包模式看上去很诡异;但是在理解了Javascript的执行上下文后,闭包模式就是一种很自然的代码编写模式。

4. 原型

由于Javascript中没有类的概念,因此也没有面向对象中的继承的概念。在完全没有任何继承机制的编程语言中,每个对象的方法都要明确设置一遍。假如Javascript没有继承机制,通过构造函数function A(name) {this.name= name;};创建了100个A的对象, 现在要为每个对象实现一个新的方法,就需要明确的为这100个对象都添加一个方法属性,当每个对象都有很多方法的时候,会带来巨大的性能开销和非常差的编码体验,使得代码编写方式最终会倒退到面向过程的方式。

实际上,Javascript从发展的最初就定位为面向原型的编程语言,通过原型实现另一种方式的继承:属性共享。如果为了和面向对象中的继承区分,Javascript中通过原型实现的应该叫做共享。

Javascript中的原型也是一个对象;原型这个词也表示一种机制。

每个对象都可以拥有一个原型,同时这个原型本身还可以有自己的原型,形成一个原型链,直到其原型为null的原型。

当获取对象的一个属性时,先在当前对象的键值对集合中查找,如果找到返回对应的值;如果没有,则在原型对象的键值对集合中查找,如果还没找到,则在原型的原型中查找,以此类推。

通过一个示例来演示原型的使用方法:

上面例子中,PersonPrototype就是原型对象,Person是构造函数,将Person的prototype属性设置为PersonPrototype对象,那么通过new Person创建的所有对象的原型都是PersonPrototype。

通过示例可以看到,修改原型中showMyName方法的定义,p.showMyName()的输出也会跟着发生变化,这个现象和上面原型的定义一致。

如果明确的为对象p设置一个showMyName方法,那么p对象的键值对集合中将也包含showMyName的定义,根据上面规则的描述的规则,p对象中定义的showMyName将被使用,忽略了原型中对showMyName的定义。这个时候,我们可以通过p.__proto__.showMyName来访问原型中的showMyName方法,但是调用p.__proto__.showMyName的时候,this关键字绑定到了原型对象PersonPrototype,由于原型对象中没有name属性的定义,因此输出“undefined!”。

接下来再定义几个Person对象:

相信上面代码的输出都不难理解了,所有Person对象都共享了原型对象的方法,除非某个Person对象中重新定义了原型中的方法。

一般来说原型对象中通常定义方法和常量,定义对象的状态值没有意义,因为多数情况下所有的对象共享一个状态值没有实际意义。

5. 结论

本文介绍了Javascript的三个核心概念:对象、执行上下文和原型。这些是Javascript最重要的内容,理解这三点之后,很快就能掌握Javascript。

Javascript代码中各类表达式与C++基本相同。

Javascript包括很多预定义的类型、对象和对象属性,比如全局对象及其属性,JSON类型和RegExp类型等等,这些查阅手册即可。

当然还有很多细节性的东西需要进一步讨论,例如:如果不明确设置Person的原型,该原型默认会是一个Person类型的对象,还有constructor属性的理解也都有一些绕。有机会将在后续章节中展开讨论。

 
 
 
标签: Javascript原型

JS核心概念的更多相关文章

  1. Vue.js核心概念

    # 1. Vue.js是什么? 1). 一位华裔前Google工程师(尤雨溪)开发的前端js库 2). 作用: 动态构建用户界面 3). 特点: * 遵循MVVM模式 * 编码简洁, 体积小, 运行效 ...

  2. TensorFlow.js之安装与核心概念

    TensorFlow.js是通过WebGL加速.基于浏览器的机器学习js框架.通过tensorflow.js,我们可以在浏览器中开发机器学习.运行现有的模型或者重新训练现有的模型. 一.安装     ...

  3. Ext JS 6学习文档–第2章–核心概念

    核心概念 在下一章我们会构建一个示例项目,而在这之前,你需要学习一些在 Ext JS 中的核心概念,这有助于你更容易理解示例项目.这一章我们将学习以下知识点: 类系统,创建和扩展类 事件 Ext JS ...

  4. js沉思录一:js的核心概念

    js的核心概念: 原型.对象(Object).函数(Function); 原型:路由路径上非叶子结点的对象: 对象:无序属性(包括函数)的集合: 函数:函数上下文的集合: 原型: 原型的创建.指定.修 ...

  5. [程序设计语言]-[核心概念]-02:名字、作用域和约束(Bindings)

    本系列导航 本系列其他文章目录请戳这里. 1.名字.约束时间(Binding Time) 在本篇博文开始前先介绍两个约定:第一个是“对象”,除非在介绍面向对象语言时,本系列中出现的对象均是指任何可以有 ...

  6. Angularjs -- 核心概念

     angularjs旨在减轻使用AJAX开发应用程序的复杂度,使得程序的创建.測试.扩展和维护变得easy.以下是angularjs中的一些核心概念. 1. client模板      多页面的应用通 ...

  7. webpack的四个核心概念介绍

    前言 webpack 是一个当下最流行的前端资源的模块打包器.当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后 ...

  8. webpack核心概念

    一.webpack四个核心概念 1.入口[Entry] webpack将创建所有应用程序 依赖关系图表.图表的起点被称之为 入口起点.入口起点告诉webpack从哪里开始,并遵循着依赖关系图表知道打包 ...

  9. Redux 核心概念

    http://gaearon.github.io/redux/index.html ,文档在 http://rackt.github.io/redux/index.html .本文不是官方文档的翻译. ...

随机推荐

  1. Tsql查询执行顺序

    对于T-SQL编程,用得最广泛的,莫过于查询(Querying).要想写出高质量.高性能的查询语句,必须深入地了解逻辑查询处理. 一.逻辑查询处理的各个阶段 (5)SELECT DISTINCT TO ...

  2. uva757 - Gone Fishing(馋)

    题目:uva757 - Gone Fishing(贪心) 题目大意:有N个湖泊仅仅有一条通路将这些湖泊相连. 每一个湖泊都会给最開始5分钟间隔内能够调到的鱼(f).然后给每过5分钟降低的鱼的数量(d) ...

  3. linux_常用命令_(ls, lsof,nslookup)_查看文件按照时间排序

    平时收集些用到的命令 方便使用 1:  ls -lrt 按时间排序  展示 2:nslookup  查看dns解析 3:lsof -p 进程号 lsof `which httpd` //那个进程在使用 ...

  4. css优先级计算规则

    原文:css优先级计算规则 最近面试了一些求职者,我问css优先级计算规则是怎样的?答曰ID优先级>class>元素选择器,外联样式优先级低于内联样式,内联样式优先级低于行间样式,然后就没 ...

  5. POJ 2109 :Power of Cryptography

    Power of Cryptography Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 18258   Accepted: ...

  6. Linux线程 之 线程 线程组 进程 轻量级进程(LWP)

    Thread Local Storage,线程本地存储,大神Ulrich Drepper有篇PDF文档是讲TLS的,我曾经努力过三次尝试搞清楚TLS的原理,均没有彻底搞清楚.这一次是第三次,我沉浸gl ...

  7. NSNotification、delegate和KVO的区别

    1.效率:delegate比nsnotification高.2. delegate方法比notification更加直接,最典型的特征是,delegate方法往往需要关注返回值, 也就是delegat ...

  8. DateTime.ToString("dd/MM/yyyy");后,不能直接Convert.ToDateTime的解决:

    原文:DateTime.ToString("dd/MM/yyyy");后,不能直接Convert.ToDateTime的解决: DateTime.ToString("dd ...

  9. 简洁vim配置方案Janus(1)

    最近不想在编辑器上花太多的精力,所以找到个不错的解决方案. 在不懂vim配置的前提下也能用的很开心. 1,下载安装Janus(https://github.com/carlhuda/janus) 安装 ...

  10. Java泛型和集合之泛型VS模板

    Java的泛型很像C++中的模板,说到Java 泛型和C++中的模板的关系时,有两个重要的方面需要被考虑到:语法和语义.语法看起来是相似的,可是语义却明显是不同的. 在语法上讲,选择尖括号  是因为他 ...