首先看看下面两个"1+1=2"的问题:

问题一:为什么改变length的值,数组的内容会变化?

var arr = [1];
arr.length = 3;
alert(arr);   // [1, undefined, undefined]

问题二:为什么在showScope函数内能访问outter,在函数外不能访问inner?

var outter = "sunshine";function showScope() {var inner = "darkness";console.log(outter);    //"sunshine"
}console.log(typeof inner)   // undefined

好了,接下来进入正文。

一、对象的属性

var person = {name: "Simon",_age: 21,isYoung: true,friends: ["Johnny", "Carlton", "Amy"],sayName: function() {console.log(this.name);}educate: {primarySch: "",highSch: "",university: ""}
};

上面的person对象是JS对象的字面量形式,本质上是一个键值对的无序集合,这些键值 对叫做属性。属性的名称只能是字符串形式的,而属性的值可以是字符串、数字、布尔值等基本类型,也可以是数组、函数、对象等引用类型。值得一提的是,如果属性的名称是JS能够识别的标识符,如name、first_name、$name,则在定义属性时不用像json那样为属性名加上引号;但属性名称是first-name这种JS无法识别的标识符时,就需要为其加上引号了。这两种情况也会造成访问方式不同,前者既可以通过person.first_name的形式访问,也可以通过person[first_name]的形式访问。但后者只能通过中括号的形式访问。

如果要对属性分类的话,属性可以分为两类:数据属性、访问器属性。这两种属性都分别有着一些特性:

数据属性
  • Configurable: 能否修改或删除属性,默认为true;

  • Enumerable: 能否通过for-in循环遍历属性,默认为true;

  • Writable: 能否修改属性的;

  • Value: 存放属性的值,默认为 undefined;

访问器属性
  • Configurable: 同上;

  • Enumerable: 同上;

  • Get: 在读取属性的值时调用的函数;

  • Set: 在设置属性的值时调用的函数;

这些特性无法直接访问,但可以通过Object.defineProperty(obj, attr, descriptor)函数定义这些特性。
基于上面的person对象各举一个例子:

// 数据属性
Object.defineProperty(person, "name", {configurable: false
})console.log(person,name); // Simon
person.name = "zai";
console.log(person,name); // Simon//访问器属性
Object.defineProperty(person, "age", {get: function() {return this._age;},set: function(newValue) {if (newValue > 30) {this._age = newValue;this.isYoung = false;}}
})

到这里第一个问题就得到了解决,数组的length属性其实就是一种访问器属性。

此外操作属性的方法还有:Object.defineProperties 用来一次定义多个属性,Object.getOwnPropertyDescriptor(obj, attr) 用来读取属性的特性。另外可以通过delete操作符去删除Configurable值为true的属性。

二、如何创建对象

仅仅通过字面量的方式去创建对象显然是不现实的,因为当我们需要创建多个相似的对象时,这样做会产生大量的重复代码。需要一种科学的方式去创建对象。

function Person(name, age, friends) {this.name = name;this.age = age;this.friends = friends;// this.prototype = { constructor: this };
}Person.prototype = {constructor: Person,sayName: function() {console.log(this.name);}
}Person.prototype.sayAge = function() {console.log(this.age);
};var simon = new Person("Simon", 22, ["Amy", "Johnny", "Carlton"]);
simon.sayName();   //委托

上面的代码结合了构造函数和原型两种方式去创建对象,首先聊聊构造函数:

构造函数

构造函数本质上还是函数,只不过为了区分将其首字母大写了而已。注意注释掉的代码是自动执行的,但这并不是构造函数独有的,每个函数在声明时都会自动生成prototype。构造函数不一样的地方在于它的调用方式——new,new调用构造函数的大致过程:

  • 产生一个新对象;

  • 将构造函数的作用域赋给新对象;

  • 执行构造函数中的代码;

  • 返回新对象或者指定返回的对象;

构造函数本质上仍是函数,所以当然可以直接调用,这样构造函数中的this就指的是全局对象,显然不符合预期。

原型

《JavaScript高级程序设计》上的一幅图很好的解释了原型、构造函数、实例之间的关系:

执行simon.sayName( )时,首先在simon对象本身的作用域中寻找sayName,没有找到之后再去其原型Person.prototype中寻找,这个过程叫做委托。那么问题就来了,当我们不知道一个对象的构成时,如何去判断一个属性属于对象还是其原型呢?obj.hasOwnProperty(propName)就是做这个事情的函数,常常被用在for-in循环遍历对象的属性的过程中,与for-in类似的两个方法:Object.keys(obj)、Object.getOwnPropertyNames(obj) 这两个方法返回的都是属性名的数组,都不包括原型中的属性,区别在于前者和for-in一样只遍历enumrable为 true的属性,而后者遍历所有属性。

三、继承

这里给出一种JavaScript实现继承的方式:

function Vehicle(maxSpeed, wheels) {this.maxSpeed = maxSpeed;this.wheels = wheels;
}Vehicle.prototype.checkMaxSpeed = function() {console.log(this.maxSpeed);
};function Car(brand, maxSpeed) {Vehicle.call(this, maxSpeed, 4);this.brand = brand;
}Car.prototype = new Vehicle();
Car.prototype.constructor = Car;
Car.prototype.checkBrand = function() {console.log(this.brand);
};var panemera = new Car("Panemera", 250);

这里的关键在于在Car中调用Vehicle,向父类构造器传递参数,初始化子类的属性,再进行扩充(brand),当然仅仅有构造函数还是不行的,还需要原型链才能更好地实现继承,这里Car的原型是Vehicle的一个实例,值得注意的是Car.prototype = new Vehicle();之后,原本的constructor丢失了,新的constructor在这里指向了Vehicle,需要重置为Car。

之前提出的第二个问题其实就是用继承来实现的:

function showScope() {// scope代表当前作用域var oldScope = scope;var Scope = function() {};//继承当前作用域Scope.prototype = scope;scope = new Scope();// 进入函数作用域,扩充作用域advance("{");parse(scope);     // 用当前作用域做解析advance("}");scope =oldScope;
}

假设showScope是解析作用域的函数,它的实现机制大概是:进入函数作用域之前保存当前作用域,新建一个继承了当前作用域的对象并用它取代当前作用域,解析左括号进入函数作用域并对当前作用域进行扩充,使用扩充后的作用域进行解析,解析右括号离开函数作用域,恢复进入函数前的作用域。

四、私有成员的实现

最后说说JavaScript中私有成员的实现,一个很有趣的例子:

function AladdinLamp() {var limit = 3;function rubLamp() {if (limit > 0) {limit -= 1;return true;} else {return false;}}this.satisfyWish = function() {return rubLamp() ? Math.random() : null;};
}

这里的limit和rubLamp都是AladdinLamp的私有成员,无法从外部直接访问,只能通过唯一暴露出来的satisfyWish调用,这实际上是一种闭包,关于闭包请参考本专栏中的浅谈JavaScript中的闭包

五、ES6中的类与继承

上文谈到的都是ES5,那么ES6有什么不同呢,先来看看ES6中的类:

class Vehicle {constructor(maxSpeed, wheels) {this.maxSpeed = maxSpeed;this.wheels = wheels;}checkMaxSpeed() {console.log(this.maxSpeed);}static openDoor() {console.log("Welcome");}
}Vehicle.length = 100;let bike = new Vehicle(40, 2);
// TypeError
bike.openDoor();

不同之处在于构造函数换成了Class,其实Class本质上也是函数,constructor就相当于ES5中的构造函数,而直接在类中声明的checkMaxSpeed实际相当于 Vehicle.prototype.checkMaxSpeed = ...
有意思的是ES6中多了静态方法的实现,这里的openDoor无法在实例中调用,可以通过Vehicle.openDoor直接调用,可以继承给子类。另外通过Vehicle.props = ...的形式可以定义静态变量。最后注意Vehicle只能通过new调用,否则会报错,是因为在constructor中检测了new.target。
再看看ES6中的继承:

class Car extends Vehicle {constructor(maxSpeed, wheels, brand) {super(maxSpeed, wheels);this.brand = brand;}checkBrand() {console.log(this.brand);}
}

继承的关键在于constructor中调用了super,即父类的构造函数。这里一定要调用super,因为子类的this是由super创建的,之后再去扩充this。

JavaScript面向对象那些事相关推荐

  1. 《JavaScript面向对象精要》读书笔记

    JavaScript(ES5)的面向对象精要 标签: JavaScript 面向对象 读书笔记 2016年1月16日-17日两天看完了<JavaScript面向对象精要>(参加异步社区的活 ...

  2. javascript面向对象系列第一篇——构造函数和原型对象

    前面的话 一般地,javascript使用构造函数和原型对象来进行面向对象编程,它们的表现与其他面向对象编程语言中的类相似又不同.本文将详细介绍如何用构造函数和原型对象来创建对象 构造函数 构造函数是 ...

  3. JavaScript面向对象编程深入分析

    JavaScript面向对象编程深入分析 一. Javascript 面向对象编程:封装 Javascript是一种基于对象(object-based)的语言,你遇到的所有东西几乎都是对象.但是,它又 ...

  4. Javascript 面向对象编程

    Javascript是一个类C的语言,他的面向对象的东西相对于C++/Java比较奇怪,但是其的确相当的强大,在 Todd 同学的"对象的消息模型"一文中我们已经可以看到一些端倪了 ...

  5. javascript 本地对象和内置对象_详解 JavaScript 面向对象

    1. 概述 JavaScript面向对象比较难理解的点是类的继承.不管是es5写法还是es6写法,JavaScript继承的本质是原型链.具体可看我的上一篇文章: 田浩:详解原型.原型链.构造函.实例 ...

  6. JavaScript面向对象--继承 (超简单易懂,小白专属)...

    JavaScript面向对象--继承 (超简单易懂,小白专属) 一.继承的概念 子类共享父类的数据和方法的行为,就叫继承. 二.E55如何实现继承?探索JavaScript继承的本质 2.1构造函数之 ...

  7. Javascript面向对象特性

    JavaScript面向对象的支持 ~~~~~~~~~~~~~~~~~~ 很少有人对JavaScript的面向对象特性进行系统的分析.我希望接下来的文字让你了解到这 个语言最少为人知的一面. 1. J ...

  8. Javascript面向对象全面剖析 —创建对象

    先介绍目前在ECMAScript中使用最广泛,认同度最高的默认模式. 1.组合使用构造函数及原型 function Person(name,age,job){this.name = name;this ...

  9. 《JavaScript 面向对象精要》 读书笔记

    <JavaScript 面向对象精要> 读书笔记 高程面向对象这块内容介绍的比较浅显,个人觉得这本小书是高程的补充,看完之后觉得收获匪浅,所以做了个笔记,以备后询 1. 原始类型和引用类型 ...

最新文章

  1. 人类史上最伟大的 PPT,马斯克的 39 页火星计划PPT
  2. PuTTy:PuTTy的简介、安装、使用方法之详细攻略
  3. 前端学习(662):逻辑运算符练习
  4. oracle 中的trunc()函数及加一个月,一天,一小时,一分钟,一秒钟方法
  5. linux tcp 监控,Zabbix 监控tcp连接的状态
  6. servlet-servletContext简述
  7. 基于编辑方法的文本生成(下)
  8. UVM组件(以APB协议为例)——UVM
  9. [免费专栏] Android安全之绕过SSL Pinning抓HTTPS数据
  10. 2021-03-04
  11. 瓦里安将携三大“全球首发”解决方案亮相 | 进博会倒计时
  12. 创建企业邮箱后如何登录邮箱?企业邮箱登陆入口在哪里?
  13. [ubuntu14.04]linux 开发装机必备
  14. Linux部署禅道在访问web页面进入www时报错:mysql无法连接(重新解压安装包或者输入命令:setenforce 0即可)
  15. 西安交通大学城市学院的计算机类专业,西安交通大学城市学院计算机系2020级专业分流会...
  16. vue+element-ui调用后台接口实现excel在线预览
  17. week06_task_二分, 排序
  18. 软件外包项目管理实务
  19. 【decode()】
  20. 二级域名,https协议的申请配置

热门文章

  1. SQLCE数据库的几点研究
  2. nyist 2 括号配对问题
  3. linux检测系统是否被入侵(下)
  4. 原生js替换jQuery各种方法-中文版
  5. UWA TIPS:让你的项目更懂你!
  6. yii2实现WebService 使用 SoapDiscovery
  7. 戴尔携手EMC战略合作续签至2013年
  8. PostgreSQL——不仅仅是监控
  9. Objective-C ---JSON 解析 和 KVC
  10. ASP.NET vNext 概述