读Secrets of the JavaScript Ninja(二)对象

面向对象和原型

理解原型

在JavaScript中,可通过原型实现继承。原型的概念很简单。每个对象都含有原型的引用,当查找属性时,若对象本身不具有该属性,则会查找原型上是否有该属性。

每个对象都可以有一个原型,每个对象的原型也可以拥有一个原型,以此类推,形成一个原型链。查找特定属性将会被委托在整个原型链上,只有当没有更多的原型可以进行查找时,才会停止查找。

对象构造器与原型

当用作为函数调用Ninja时,什么都不会做。在用new操作符时返回一个对象,并且设置了它的原型为Ninja,所以ninja2可以调用swingSword方法。但swingSword方法是Ninja的原型属性, 而不是ninja实例的属性

function Ninja(){}
Ninja.prototype.swingSword = function(){
  return true;
};
const ninja1 = Ninja();
assert(ninja1 === undefined,
"No instance of Ninja created.");
const ninja2 = new Ninja();
assert(ninja2 &&
ninja2.swingSword &&
ninja2.swingSword(),
"Instance exists and method is callable." );

实例属性

  1. 当在原型和实例中有重名属性时,实例属性优先级比原型属性高
  2. 创建了多个实例,每个实例都是独立的拷贝,但是原型引用的是同一个方法
function Ninja(){
  this.swung = false;
  this.swingSword = function(){
    return !this.swung;
  };
}
Ninja.prototype.swingSword = function(){
  return this.swung;
};
const ninja = new Ninja();
assert(ninja.swingSword(),
"Called the instance method, not the prototype method.");

通过构造函数实现对象类型

constructor

通过constructor属性来检查类型

assert(ninja.constructor === Ninja, constructor引用检测ninja的类型,得到的结果为其构造函数的引用

使用constructor的引用创建新对象

const ninja2 = new ninja.constructor(); ?--- 通过第1个实例化对象的constructor方法创建第2个实例化对象

实现继承

为了实现继承,将Person的实例作为Ninja的原型,所以当Niaja药调用person的方法时,将会沿着原型链进行查找。

function Person(){}
Person.prototype.dance = function(){};
function Ninja(){}
Ninja.prototype = new Person(); ?--- 通过将Ninja的原型赋值为Person的实例,实现Ninja继承Person
const ninja = new Ninja();
assert(ninja instanceof Ninja,
"ninja receives functionality from the Ninja prototype");
assert(ninja instanceof Person, "... and the Person prototype");
assert(ninja instanceof Object, "... and the Object prototype");
assert(typeof ninja.dance === "function", "... and can dance!")

重写constructor属性的问题

通过设置Person实例对象作为
Ninja构造器的原型时, 我们已经丢失了Ninja与Ninja初始原型之间的关联。

//通过defineProperty配置constructor对象
function Person(){}
  Person.prototype.dance = function(){};
  function Ninja(){}
  Ninja.prototype = new Person();
  Object.defineProperty(Ninja.prototype,"constructor",{
    enumerable: false,
    value: Ninja,
    writable: true
  }
);

在ES6使用JavaScript的class

ES6中使用关键字class来实现类,但其底层的实现仍然是基于原型继承!

使用class关键字

class Ninja{
  constructor(name){
    this.name = name;
  }s
  wingSword(){
    return true;
  }
  //静态方法
  static compare(ninja1, ninja2){
    return ninja1.level - ninja2.level;
  }
}

实现继承

class Person {
  constructor(name){
    this.name = name;
  }
  dance(){
    return true;
  }
}
class Ninja extends Person
  constructor(name, weapon){
    super(name); ?--- 使用关键字super调用基类构造函数
    this.weapon = weapon;
  }
  wieldWeapon(){
    return true;
  }
}

控制对象的访问

使用getter与setter控制属性访问

//使用字面量get set
const ninjaCollection = {
  ninjas: ["Yoshi", "Kuma", "Hattori"],
  get firstNinja(){
    report("Getting firstNinja");
    return this.ninjas[0];
  }, ?--- 定义firstNinja的getter方法, 返回ninjas列表中第一个值, 并记录一条
  消息
  set firstNinja(value){
    report("Setting firstNinja");
    this.ninjas[0] = value;
  } ?--- 定义firstNinja的setter方法, 设置ninjas列表中第一个值, 并记录一条
  消息
};

//ES6 class
class NinjaCollection {
  constructor(){
    this.ninjas = ["Yoshi", "Kuma", "Hattori"];
  }
  get firstNinja(){
    report("Getting firstNinja");
    return this.ninjas[0];
  }
  set firstNinja(value){
    report("Setting firstNinja");
    this.ninjas[0] = value;
  } ?--- 在ES6的class中使用getter和setter
}
const ninjaCollection = new NinjaCollection();

使用getter与setter校验属性值

function Ninja() {
  let _skillLevel = 0;
  Object.defineProperty(this, 'skillLevel', {
    get: () => _skillLevel,
    set: value => {
    if(!Number.isInteger(value)){
      throw new TypeError("Skill level should be a number");
      } ?--- 校验传入的值是否是整型。 如果不是, 则抛出异常
    _skillLevel = value;
    }
  });
}
const ninja = new Ninja();

使用代理控制访问

可以将代理理解为通用化的setter与getter,区别是每个setter与getter仅能控制单个对象属性, 而代理可用于对象交互的通用处理,包括调用对象的方法

通过Proxy构造器创建代理

const emperor = { name: "Komei" };
const representative = new Proxy(emperor, {
  get: (target, key) => {
  report("Reading " + key + " through a proxy");
    return key in target ? target[key]
      : "Don't bother the emperor!"
  },
  set: (target, key, value) => {
    report("Writing " + key + " through a proxy");
    target[key] = value;
  }
});

使用代理记录日志

function makeLoggable(target){
  return new Proxy(target, {
    get: (target, property) => {
      report("Reading " + property);
      return target[property];},
    set: (target, property, value) => {
      report("Writing value " + value + " to " + =property);
      target[property] = value;
  }
});
}
let ninja = { name: "Yoshi"};
ninja = makeLoggable(ninja);
assert(ninja.name === "Yoshi", "Our ninja Yoshi");
ninja.weapon = "sword"; ?--- 对代理对象进行读写操作时, 均会通过代理方法记录日志

使用代理可以优雅地实现以下内容。

  • 日志记录。
  • 性能测量。
  • 数据校验。
  • 自动填充对象属性(以此避免讨厌的null异常) 。
  • 数组负索引。

原文地址:https://www.cnblogs.com/secoding/p/11161397.html

时间: 2024-11-09 01:02:09

读Secrets of the JavaScript Ninja(二)对象的相关文章

JavaScript事件---事件对象

原文:JavaScript事件---事件对象 发文不易,若转载传播,请亲注明出处,谢谢!   内容提纲: 1.事件对象 2.鼠标事件 3.键盘事件 4.W3C与IE JavaScript事件的一个重要方面是它们拥有一些相对一致的特点,可以给你的开发提供更多的强大功能.最方便和强大的就是事件对象,他们可以帮你处理鼠标事件和键盘敲击方面的情况,此外还可以修改一般事件的捕获/冒泡流的函数. 一.事件对象 事件处理函数的一个标准特性是,以某些方式访问的事件对象包含有关于当前事件的上下文信息. 事件处理三

JavaScript日期时间对象的创建与使用(三)

时钟效果一: 代码: <html> <head> <meta charset="utf-8"/> <title>JavaScript日期时间对象的创建与使用</title> </head> <body> <h2 id="time"></h2> <script type="text/javascript"> function Cl

初探JavaScript(二)——JS如何动态操控HTML

除去五一三天,我已经和<JavaScript Dom编程艺术>磨合了六天,第一印象很好.慢慢的,我发现这是一块排骨,除了肉还有骨头.遇到不解的地方就会多看几遍,实在不懂的先跳过,毕竟,初次接触JS,没有必要花费过多时间去钻死胡同,先混个脸熟,以后再来拜访也未尝不可嘛.就这样,踉踉跄跄.囫囵吞枣似的已经过五关斩六将,到达第十一章. 书中有几个章节并没有从语法.技术等层面介绍JavaScript,而是站在一个全局的角度,立足编程原则和习惯道破了我们该如何看待和使用这门语言,主要有以下几点: Jav

Head first javascript(二)

three basic data types text number boolean 变量 var var_name; 用'='初始化变量 var var_name = ini_value; const (不是所有浏览器都支持const) const const_name = ini_value; nan: not a number 使用一些未初始化的变量进行计算的时候会产生nan,如: const unknown; total = (1 + 2) * unknown; 这里total在计算的时

Ext JS学习第三天 我们所熟悉的javascript(二)

•javascript之函数 •对于Ext开发者,我还是希望你能对javascript原生的东西非常了解.甚至熟练掌握运用.那么函数,无疑是非常重要的概念.首先在前面一讲,我们知道了函数也是一种数据类型,创建函数一共有三种方式.每种方式他们都会有区别,分别为: –function语句形式 –函数直接量形式 –构造函数形式 •函数中的arguments对象 –argument的作用一:接受函数的实际参数 –argument的作用二:用于做递归操作 栗子代码 1 //Function 函数 2 //

JavaScript内置对象(一)

一.什么是对象     1.什么是对象:JavaScript中的所有事物都是对象:字符串.数值.数组.函数... 每个对象带有属性和方法 JavaScript允许自定义对象 2.自定义对象: 定义并创建对象实例 使用函数来定义对象,然后创建新的对象实例 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> </head>

javascript中Date对象的应用——简易日历的实现

× 目录 [1]效果 [2]HTML [3]CSS[4]JS 前面的话 简易日历作为javascript中Date对象的常见应用,用途较广泛.本文将详细说明简易日历的实现思路 效果演示 HTML说明 使用type=number的两个input分别作为年和月的输入控件,这样在高级浏览器下自带调节按钮 按照周日到周一的顺序进行星期的排列 <div class="box"> <header class='control'> <input id="con

javascript 作用域 闭包 对象 原理和示例分析(上)

                                                                                             阅读.理解.思考.实践,再实践.再思考....  深圳小地瓜献上 javascript高级特性包含:作用域.闭包.对象 -----------------------------------------------作用域-----------------------------------------------

JavaScript内置对象,Date String Array等,以及这些对象操作。

练习题: 某班的成绩出来了,现在老师要把班级的成绩打印出来.效果图:2014年5月9日 星期六--班级总分为: 班级平均分为:格式要求:1.显示打印的日期. 格式为类似“2014年03月21日 星期三” 的当前的时间.2.计算出该班级的平均分(保留整数).同学成绩数据如下:"小明:87; 小花:81; 小红:97; 小天:76;小张:74;小小:94;小西:90;小伍:76;小迪:64;小曼:76"任务第一步:可通过javascript的日期对象来得到当前的日期.提示:使用Date()