资讯专栏INFORMATION COLUMN

理解 JavaScript(一)

longshengwang / 1795人阅读

摘要:中的首先,是一个操作符,它可以用来创建两种对象的实例,一种是用户定义的对象类型,另一种则是拥有构造函数的内建对象类型。这就是原型继承构造函数被调用并传入指定的参数示例二中的,然后被绑定给新创建的对象。

JavaScript 中的 new

首先,new 是一个操作符,它可以用来创建两种对象的实例,一种是用户定义的对象类型,另一种则是拥有构造函数的内建对象类型。

创建用户定义的对象需要两个步骤:

通过编写函数来定义对象类型;

使用 new 来创建对象实例。


示例一:

var Person = function(personName) {
    this.name = personName;
};

这是一个典型的通过编写函数来定义对象类型的范例,我们可以这样来表述其行为:

  

Person 函数定义了一种对象类型,其 类型名称 就叫 Person
在使用 Person 函数创建对象实例的时候可以传入变量 personName,该变量会成为对象实例的一个属性,这个属性的名字叫 name
为对象实例定义 name 属性的过程发生在 Person 函数的函数体内;this 即指代将被创建的对象实例。

值得初学者注意的是,在现实中你更多地会看到这样的代码:

var Person = function(name) {
    this.name = name;
};

有些人会搞不清楚究竟哪一个 name 才是对象的属性,在这里详细解释如下:

function(name) 里的 name待传入参数的名字,通常被称作:形式参数(Formal Parameter),或简称 形参 ——因为它只是代表参数的形式而并非真正传入的参数(后者则被称作:实际参数(Actual Parameter),或简称 实参)。

this.namename 是对象的属性名字。

= namename 还是形参,和 1. 里的 name 等价;这就是所谓的 参数传递,或传参


示例二:

var albert = new Person("Albert");
albert.name;       // "Albert"
albert["name"];    // "Albert"

这是承接示例一,使用定义好的 Person 对象类型来实例化对象的范例,这个范例的表述相对容易一些:

  

定义一个变量 albert,然后实例化一个新的 Person 类型的对象,并将变量 albert 指向这个新的对象。

如果你对 形参实参 还不够清楚的话,看到这里就应该完全明了了。保险起见再加以解释如下:

在示例二中,new Person("Albert") 中的 "Albert" 即对应着示例一中 function(name) 中的 name,同时也是接下来一行中等号右边的 name

因此,"Albert" 就是 实际参数name 就是 形式参数

示例二中还演示了两种对象属性的获取方法,分别为 object.propertyobject["property"]。前一种比较常用,不过后一种由于可以用字符串来访问对象属性,因此在某些场合下非常有用(比如说用字符串传递了对象的属性)。


回到 new 的话题。

new Person("Albert") 执行的时候,会有如下事情发生:

创建一个新的对象,其类型是 Person 并继承 Person.prototype 的所有属性。这就是 原型继承

构造函数 Person 被调用并传入指定的参数(示例二中的 "Albert"),然后 this 被绑定给新创建的对象。另外,若构造函数不需要参数,则 new Person 等价于 new Person()

若构造函数没有明确的返回值,那么新创建的对象就是整个 new 表达式的结果;反之,若构造函数内显式定义了返回值,则该返回值为整个 new 表达式的结果。

关于第三点,举例示之:

示例三:

var Person = function(name) {
    return {
        name: "Mr. " + name
    }
};

var albert = new Person("Albert");

albert.name;        // "Mr. Albert"
var Person = function(name) {
    return {
        rawName: name,
        getName: function(gender) {
            if (gender === "male") {
                return "Mr. " + name;
            } else {
                return "Mrs. " + name;
            }
        }
    }
};

var albert = new Person("Albert");

albert.getName("male");        // "Mr. Albert"
albert.getName("female");      // "Mrs. Albert"
var Person = function(name, gender) {
    return {
        rawName: name,
        name: (function() {
            if (gender === "male") {
                return "Mr. " + name;
            } else {
                return "Mrs. " + name;
            }
        }())
    }
};

var albert = new Person("Albert", "male");
albert.rawName;             // "Albert"
albert.name;                // "Mr. Albert"

var annie = new Person("Annie", "female");
annie.rawName;              // "Annie"
annie.name;                 // "Mrs. Annie"

示例三演示了三种看起来相似但实际上具有显著差异的对象类型定义和对象实例化的例子:

第一种:在 new Person("Albert") 时返回自定义的对象,而不是默认由 new 创建的新对象。在这个自定义对象里,没有简单地把参数 name 赋给属性 this.name,而是做了进一步的修改。这种修改很显然是非常简单但却不够灵活,为了改进它,看下面两个例子:

第二种:同样返回自定义对象,这一次定义了两个属性,一个是 rawName,保存实例化时传递的参数;另一个是 getName,它是一个函数声明,因此不能直接用 albert.getNamealbert["getName"] 来访问(只会返回函数声明本身,但不会有返回值)。不过你可以用albert.getNam("male")albert["getName"]("male") 的方式来执行这个函数并求得结果,这就是所谓的 方法
如果不想用方法调用,但仍然希望像方法声明体内那样做一些逻辑判断是否可以呢?可以,继续看第三种:

第三种:这一次 name 属性又可以像以前那样直接访问了,原因是 name 指向的函数声明使用了 IIFE(Immediately Invoked Function Expression) 技巧,该技巧使得函数声明直接转变成了函数表达式(并即刻执行)。我们知道,函数表达式是能够直接返回值的,而函数声明则需要执行(调用)才能返回值,于是 name 属性获得了返回值,就可以像原来那样直接访问了。
这种属性定义方式有时被称之为 计算后属性(Computed Property),顾名思义:不是直接返回实例化时传递的值,而是对值进行了一定的处理(计算)之后才返回。


  

属性与方法:很多人都以为对象有 属性方法,其中属性是可以直接访问到值的,而方法是需要执行才能获得值的。但有的时候也会听到“方法也是属性”这样的说法,这是为什么呢?
其实原因在于对术语的翻译不够准确。英文里的 propertyattribute 都被我们翻译为属性,然而在谈及对象时这两者是不同的。在一个对象里,attributemethod 被统称为 property,直接保存值的 property 称之为 attribute,保存函数声明可以用来执行的 property 才是 method。在用中文描述时很容易把两种“属性”搞混,需要注意分辨清楚。

补充说明:本文发表出去之后,有人感谢我帮他分清了 属性方法 在一些书中的歧义性,也有人拿着别的书来向我表示疑惑。以再版的 Object Oriented in JavaScript 为例,该书中在讲解面向对象基础的时候,明确地指出:保存数据的属性叫做 Property,保存行为的属性叫做 Method,根本就不使用 Attribute。这倒是也简单明了,这样一来就不存在 Method 也是 Property 一说了。

老实说,对此我也不知该如何回应。不同的书用不同的术语,不同的作者也有不同的理解,我没有“统一业界术语”的能量,所以也只能把它们一一列举出来。对于初学者若造成理解上的偏差我表示道歉,总而言之你要记住:一个对象有两类东西:一类记录数据,另一类记录方法(方法总是做一件什么事,其中也包括返回新的数据),至于这两类在不同的情境中叫法不一,也就要靠你自己去分辨了。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/77968.html

相关文章

  • 名【合格】前端工程师的自检清单

    摘要:在他的重学前端课程中提到到现在为止,前端工程师已经成为研发体系中的重要岗位之一。大部分前端工程师的知识,其实都是来自于实践和工作中零散的学习。一基础前端工程师吃饭的家伙,深度广度一样都不能差。 开篇 前端开发是一个非常特殊的行业,它的历史实际上不是很长,但是知识之繁杂,技术迭代速度之快是其他技术所不能比拟的。 winter在他的《重学前端》课程中提到: 到现在为止,前端工程师已经成为研...

    罗志环 评论0 收藏0
  • 名【合格】前端工程师的自检清单

    摘要:在他的重学前端课程中提到到现在为止,前端工程师已经成为研发体系中的重要岗位之一。大部分前端工程师的知识,其实都是来自于实践和工作中零散的学习。一基础前端工程师吃饭的家伙,深度广度一样都不能差。开篇 前端开发是一个非常特殊的行业,它的历史实际上不是很长,但是知识之繁杂,技术迭代速度之快是其他技术所不能比拟的。 winter在他的《重学前端》课程中提到: 到现在为止,前端工程师已经成为研发体系...

    isaced 评论0 收藏0
  • 理解javascript核心知识点

    摘要:作用域链的作用就是做标示符解析。事件循环还有个明显的特点单线程。早期都是用作开发,单线程可以比较好当规避同步问题,降低了开发门槛。单线程需要解决的是效率问题,里的解决思想是异步非阻塞。 0、前言 本人在大学时非常痴迷java,认为java就是世界上最好的语言,偶尔在项目中会用到一些javascript,但基本没放在眼里。较全面的接触javascript是在实习的时候,通过这次的了解发现...

    laznrbfe 评论0 收藏0
  • JavaScript 异步

    摘要:从最开始的到封装后的都在试图解决异步编程过程中的问题。为了让编程更美好,我们就需要引入来降低异步编程的复杂性。写一个符合规范并可配合使用的写一个符合规范并可配合使用的理解的工作原理采用回调函数来处理异步编程。 JavaScript怎么使用循环代替(异步)递归 问题描述 在开发过程中,遇到一个需求:在系统初始化时通过http获取一个第三方服务器端的列表,第三方服务器提供了一个接口,可通过...

    tuniutech 评论0 收藏0
  • 前端练级攻略(第二部分)

    摘要:是文档的一种表示结构。这些任务大部分都是基于它。这个实践的重点是把你在前端练级攻略第部分中学到的一些东西和结合起来。一旦你进入框架部分,你将更好地理解并使用它们。到目前为止,你一直在使用进行操作。它是在前端系统像今天这样复杂之前编写的。 本文是 前端练级攻略 第二部分,第一部分请看下面: 前端练级攻略(第一部分) 在第二部分,我们将重点学习 JavaScript 作为一种独立的语言,如...

    BWrong 评论0 收藏0
  • 前端基础进阶():内存空间详细图解

    摘要:一栈数据结构与不同,中并没有严格意义上区分栈内存与堆内存。引用数据类型的值是保存在堆内存中的对象。不允许直接访问堆内存中的位置,因此我们不能直接操作对象的堆内存空间。为了更好的搞懂变量对象与堆内存,我们可以结合以下例子与图解进行理解。 showImg(https://segmentfault.com/img/remote/1460000009784102?w=1240&h=683); ...

    _Suqin 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<