当前位置: 首页 > news >正文

JavaScript总结3

为什么80%的码农都做不了架构师?>>>   hot3.png

类和模块

类和原型

  1. JavaScript中, 类的所有实例对象都从同一个原型对象上继承属性的. 我们可以使用Object.create()来实现:
function range(from, to) {

  var r = Object.create(range.method);

  r.from = from;

  r.to = to;

  return r;

}

range.method = {

  includes: function(x) {

    return this.from <= x && x <= this.to;

  },

  foreach: function(f) {

    for (var x = Math.ceil(this.from); x <= this.to; x++) f(x);

  },

  toString: function() { return "(" + this.from + "..." + this.to + ")"; }

};

 

var r = range(1, 3);

// true

console.log(r.includes(2));

// 1 2 3

r.foreach(console.log);

// (1...3)

console.log(r.toString());

类和构造函数

  1. 构造函数是用来初始化新创建的对象的. 调用构造函数的一个重要特征是: 构造函数的prototype属性被用来做新对象的原型. 这意味着通过同一个构造函数创建的所有对象都继承自同一个相同的对象, 因此它们都是同一个类的成员.
  2. 定义的构造函数名字首字母通常要大写
function Range(from, to) {

  this.from = from;

  this.to = to;

}

Range.prototype = {

  includes: function(x) {

    return this.from <= x && x <= this.to;

  },

  foreach: function(f) {

    for (var x = Math.ceil(this.from); x <= this.to; x++) f(x);

  },

  toString: function() { return "(" + this.from + "..." + this.to + ")"; }

};

 

var r = new Range(1, 3);

// true

console.log(r.includes(2));

// 1 2 3

r.foreach(console.log);

// (1...3)

console.log(r.toString());
  1. instanceof用于检查一个实例对象是否继承至某种原型:
r instanceof Range // 如果r继承自Range.prototype, 则返回true

这里, instanceof并不会检查r是否由Range()构造函数初始化而来, 而会检查r是否继承自Range.prototype. 所以更原始检测原型的方法是:

Range.prototype.isPrototypeOf(r) // 检查r的原型是否为Range.prototype

     但上例中存在一定的错误, 当我们执行以下代码时候:

r.constructor.prototype == Range.prototype ==> false

     这里是false, 是因为我们并没有给Range的constructor增加Range.

 

  1. 每个函数都具有constructor属性, 用于指明此函数是如果构建的:
var F = function(){}

F.constructor == F.prototype.constructor ==> false

F.constructor ==>

function Function() { [native code] }

F.prototype.constructor ==>

function (){}

所以, 针对我们之前所编写的Range.prototype, 我们由于给Range.prototype重新赋值了, 所以需要添加上constructor:

Range.prototype = {

  constructor: Range,

  ...

}

或者我们对Range.prototype进行扩充, 则无需添加constructor:

Range.prototype.includes = function(x) { ... }

JavaScript中的Java式继承

  1. Range的例子中完美的诠释了继承: 构造函数对象(Range), 原型对象(Range.prototype), 实例对象(new Range(1, 3)).
  2. 其中, 原型对象中的所有方法和字段, 均会被构造函数对象中同名的方法和字段所覆盖.
  3. 每个实例对象具有自己特有的函数和方法(来自构造函数对象中所定义的), 而共享的函数和方法来自原型对象中.
  4. 所以, 一般将通用的方法放在原型对象中, 而原型对象中通常不存放字段.

类的扩充

     JavaScript中基于原型的继承机制是动态的: 对象从其原型继承属性, 如果创建对象之后原型的属性发生改变, 也会影响到继承这个原型的所有实例对象.

var o = {

  show: function() {

    console.log("show");

  }

};

var sub_o = Object.create(o);

o.play = function() {

  console.log("play");

};

// play

sub_o.play();

// show play

for (var k in sub_o) {

  console.log(k);

}

     这里, 给原型添加属性, 默认情况下是可枚举的; 在ECMA5下, 可以使用Object.defineProperty()设置为不可枚举, 但不能保证所运用的Web浏览器支持其defineProperty().

     所以, 一般我们不推荐给Object.prototype添加方法, 或者给具体的类如String.prototype/Array.prototype添加方法, 也是基于这种考虑的.

类和类型

  1. instanceof: 如果o继承自c.prototype, 则表达式o instanceof c值为true.

构造函数是类的公共标识, 但原型是唯一的标识. 尽管instanceof运算符的右操作数是构造函数, 但计算过程实际上是检测了对象的继承关系, 而不是检测创建对象的构造函数.

  1. isPrototypeOf: 检测对象的原型链上是否存在某个特定的原型对象:
range.methods.isPrototypeOf(r); // range.method 是原型对象

instanceof/isPrototypeOf的不足之处在于两点: 1是我们无法确切知道(o instanceof c)中o的具体类名; 2是在多窗口多框架的子页面中, Array()并不相等.

  1. constructor: 用于指明对象是如何构建的.

备注: instanceof和constructor都无法用来检测对象是因为, 它们在多个执行上下文中是不同的.

  1. 构造函数的名字: 如果一个函数具有名字, 则无论在不同的上下文中, 它们均是相同的:
function type(o) {

  var t, c, n;

  if (o === null) return "null";

  if (o !== o) return "nan";

  if ((t = typeof o) !== "object") return t;

  if ((c = classof(o)) !== "Object") return c;

  if (o.constructor && typeof o.constructor === "function"

      && (n = o.constructor.getName())) return n;

 

  return "Object";

}

 

function classof(o) {

  return Object.prototype.toString.call(o).slice(8, -1);

}

 

Function.prototype.getName = function() {

  if ("name" in this) return this.name;

  return this.name = this.toString().match(/function\s*([^(]*)\(/)[1];

}

var o = Object.create(Array);

// Function

console.log(type(o));

JavaScript中的面向对象技术

  1. 一个集合类
function Set() {

  this.values = {};

  this.n = 0;

  this.add.apply(this, arguments);

}

Set.prototype.add = function() {

  for (var i = 0; i < arguments.length; i++) {

    var val = arguments[i];

    var str = Set._v2s(val);

    if (!this.values.hasOwnProperty(str)) {

      this.values[str] = val;

      this.n++;

    }

  }

  return this;

};

Set.prototype.remove = function() {

  for (var i = 0; i < arguments.length; i++) {

    var str = Set._v2s(arguments[i]);

    if (this.values.hasOwnProperty(str)) {

      delete this.values[str];

      this.n--;

    }

  }

  return this;

};

Set.prototype.contains = function(value) {

  return this.values.hasOwnProperty(Set._v2s(value));

};

Set.prototype.size = function() {

  return this.n;

};

Set.prototype.foreach = function(f, context) {

  for (var s in this.values) {

    if (this.values.hasOwnProperty(s)) {

      f.call(context, this.values[s]);

    }

  }

};

Set.prototype.toString = function() {

  var _arr = [];

  for (var k in this.values) {

  _arr.push(this.values[k]);

  }

  console.log('' + _arr);

}

Set._v2s = function(val) {

  switch (val) {

    case undefined: return 'u';

    case null: return 'n';

    case true: return 't';

    case false: return 'f';

    default: switch (typeof val) {

      case 'number': return '#' + val;

      case 'string': return '"' + val;

      default: return '@' + objectId(val);

    }

  }

  function objectId(o) {

    var prop = "|**objectid**|";

    if (!o.hasOwnProperty(prop)) {

      o[prop] = Set._v2s.next++;

    }

    return o[prop];

  }

};

Set._v2s.next = 100;

 

var set = new Set(1, 2, 3, 2, 1);

// 1,2,3

set.toString();

set.add(3, 4, 5);

// 1,2,3,4,5

set.toString();

set.remove(1, 2);

// 3,4,5

set.toString();

// true

console.log(set.contains(4));
  1. 枚举类型
function enumeration(namesToValues) {

  var enumeration = function() { throw "can't Instantiate Enumerations"; };

 

  var proto = enumeration.prototype = {

    constructor: enumeration,

    toString: function() { return this.name; },

    valueOf: function() { return this.value; },

    toJSON: function() { return this.name; }

  };

 

  enumeration.values = [];

  for (var name in namesToValues) {

    var e = Object.create(proto);

    e.name = name;

    e.value = namesToValues[name];

    enumeration[name] = e;

    enumeration.values.push(e);

  }

 

  enumeration.foreach = function(f, c) {

    for (var i = 0; i < this.values.length; i++) f.call(c, this.values[i]);

  }

 

  return enumeration;

}

 

var Coin = enumeration({Penny: 1, Nickel: 5, Dime: 10, Quarter: 25});

var c = Coin.Dime;

// true

console.log(c instanceof Coin);

// true

console.log(c.constructor == Coin);

// 40

console.log(Coin.Quarter + 3 * Coin.Nickel);

// true

console.log(Coin.Dime == 10);

// true

console.log(Coin.Dime > Coin.Nickel);

// Dime:10

console.log(String(Coin.Dime) + ":" + Coin.Dime);

1) 之所以要在开头编写:

var enumeration = function() { ... }

是因为防止如下的调用:

// "can't Instantiate Enumerations

Coin();

本身, enumeration为一个类型, 而非一个函数.

2) 在enumeration中的每一个元素均为proto的继承类型, 在proto中还定义了toString()/valueOf()/toJSON的方法. 例如对proto进行计算时候, 如:

Coin.Quarter + 3 * Coin.Nickel

本身调用的是proto的valueOf()方法, 而调用:

String(Coin.Dime)

本身调用的是proto的toString()方法.

  1. 标准转换方法

toString(): 返回一个可以表示这个对象的字符串. 在希望用到字符串的地方用到对象的话, JavaScript会自动调用这个方法.

valueOf(): 将对象转换为原始值, 例如进行数学运算符/关系运算符作用于数字文本表示的对象时候, 则自动调用这个方法.

toJSON(): 调用JSON.stringify()时候自动调用.

  1. 私有状态

如果想要变量为私有, 则可以运用闭包特性(在实际的项目中, 很少使用)

function Range(from, to) {

  this.from = function() { return from; };

  this.to = function() { return to; };

}

子类

一般使用Object.create()来创建子类(ECMA5中定义的方法).

var super_o = {

  _x: undefined,

  _y: undefined,

  add: function() {

  /* ... */

  }

};

var sub_1 = Object.create(super_o);

var sub_2 = Object.create(super_o);

sub_1.sub = function() {

  /* ... */

};

sub_2.mul = function() {

  /* ... */

};

这里, super_o本身为一个prototype原型, 它提供了共享的add方法和_x/_y属性. 但一般情况下数据属性不应该被共享, 而应该绑定到具体的实例中, 所以可修改如下:

var super_o = {

  add: function() {

  return this._x + this._y;

  }

};

var sub_1 = Object.create(super_o);

sub_1._x = 1;

sub_1._y = 2;

// 3

console.log(sub_1.add());

或者如教科书般的写法:

function Super(x, y) {

  this._x = x;

  this._y = y;

}

Super.prototype = {

  add: function() {

  return this._x + this._y;

  }

};

var sub_1 = new Super(1, 2);

console.log(sub_1.add());

ECMAScript5中的类

  1. 通过defineProperty()来定义对象不可枚举:
var arr = [1, 2, 3];

arr.show = function() {

  console.log('' + this);

};

// 0 1 2 show

for (var k in arr) {

  console.log(k);

}

 

Object.defineProperty(arr, "show", {

  writable: true, // 可修改

  enumerable: false, // 不可枚举

  configurable: true, // 可删除

});

arr.show = 11;

// 11

console.log(arr.show);

// 0 1 2

for (var k in arr) {

  console.log(k);

}
  1. 定义get/set来封装对象状态
var o = {

  _x: undefined,

  get x() {

  return this._x;

  },

  set x(value) {

  this._x = value;

  }

};

o.x = 123;

// 123

console.log(o.x);

 

Object.defineProperty(o, "y", {

  get: function() { return this._y; },

  set: function(value) { this._y = value; }

});

o.y = 321;

// 321

console.log(o.y);
  1. 使用Object.preventExtensions()将对象设置为不可扩展, 也就是不能给对象添加任何新属性; Object.seal()不仅不能添加新属性, 而且已有的属性不可配置.

转载于:https://my.oschina.net/voler/blog/807510

相关文章:

  • 一个绚丽的downloading动效分析与实现!
  • 判断2的幂次方(多种算法)
  • VMware中装Win2012并配置Hyper-v
  • MySQL运维之神奇的参数
  • IOS技能点
  • 远离DoS攻击 Windows Server 2016发布DNS政策
  • Linux学习总结(22)——CentOS7.2安装Nginx
  • JS根据经纬度获取地址信息
  • 源码解读之工具--Source Insight
  • 如果就
  • Hydra用户手册
  • ABP学习日记1
  • 小博老师解析Java核心技术 ——JSwing高级菜单制作
  • Java JDBC中,MySQL字段类型到JAVA类型的转换
  • bzoj3894: 文理分科
  • IE9 : DOM Exception: INVALID_CHARACTER_ERR (5)
  • 【EOS】Cleos基础
  • 【前端学习】-粗谈选择器
  • 【跃迁之路】【519天】程序员高效学习方法论探索系列(实验阶段276-2018.07.09)...
  • JS+CSS实现数字滚动
  • js中的正则表达式入门
  • Magento 1.x 中文订单打印乱码
  • Node + FFmpeg 实现Canvas动画导出视频
  • Nodejs和JavaWeb协助开发
  • Puppeteer:浏览器控制器
  • seaborn 安装成功 + ImportError: DLL load failed: 找不到指定的模块 问题解决
  • Sequelize 中文文档 v4 - Getting started - 入门
  • 从零开始学习部署
  • 动手做个聊天室,前端工程师百无聊赖的人生
  • 高度不固定时垂直居中
  • 工作踩坑系列——https访问遇到“已阻止载入混合活动内容”
  • 观察者模式实现非直接耦合
  • 聊聊directory traversal attack
  • 前端学习笔记之原型——一张图说明`prototype`和`__proto__`的区别
  • 入口文件开始,分析Vue源码实现
  • 数组大概知多少
  • 问:在指定的JSON数据中(最外层是数组)根据指定条件拿到匹配到的结果
  • 吴恩达Deep Learning课程练习题参考答案——R语言版
  • 在electron中实现跨域请求,无需更改服务器端设置
  • hi-nginx-1.3.4编译安装
  • ​决定德拉瓦州地区版图的关键历史事件
  • ​用户画像从0到100的构建思路
  • #define与typedef区别
  • #pragma pack(1)
  • #免费 苹果M系芯片Macbook电脑MacOS使用Bash脚本写入(读写)NTFS硬盘教程
  • (1综述)从零开始的嵌入式图像图像处理(PI+QT+OpenCV)实战演练
  • (Mirage系列之二)VMware Horizon Mirage的经典用户用例及真实案例分析
  • (pojstep1.1.1)poj 1298(直叙式模拟)
  • (算法)Game
  • (转)c++ std::pair 与 std::make
  • (转)Groupon前传:从10个月的失败作品修改,1个月找到成功
  • (转)我也是一只IT小小鸟
  • *** 2003
  • ***通过什么方式***网吧
  • .net core 6 redis操作类