FE.ES-理解ECMA Javascript的原型

2018-12-07 admin

名词解析

字面量 对象字面量 var a = {}; 数组字面量 var arr = [1,2,3]; 正则表达式字面量 var reg = /[a-z]/g;

属性描述符

  • configurable:fasle是一个单向操作,同时阻止使用 delete 操作符移除既存属性的能力。
  • enumerable控制是否在属性枚举操作中出现,比如 for..in 循环。
  • writeable:false时,会阻止同名属性在 [[Prototype]] 链的低层被创建(遮蔽)。不能使用 = 赋值,而必须使用 Object.defineProperty(…)
  • 如果一个 foo 在 [[Prototype]] 链的高层某处被找到,而且它是一个 setter,那么这个 setter 总是被调用。没有 foo 会被添加到(也就是遮蔽在)myObject 上,这个 foo setter 也不会被重定义。
  • 对象常量: writable:falseconfigurable:false 组合
  • 防止扩展: Object.preventExtensions(..)
  • 封印: Object.seal(..) 等同于 防止扩展+configurable:false
  • 冻结: Object.freeze(..) 等同于 封印+writable:false

例:创建一个可写的,可枚举的,可配置的属性p

var o2 = {};
Object.defineProperties(o2, {
  'p': {value: 2,   writable: true,  enumerable: true,  configurable: true },
});
Object.getOwnPropertyDescriptor( o2, "p" );
o2 = Object.create({}, {
  p: {value: 2,  writable: true,   enumerable: true,   configurable: true },
});
o2.hasOwnProperty( "p" )

原型

prototype(显式原型) 所有的函数默认都会得到一个公有的,不可枚举的属性,称为 prototype,它可以指向任意的对象。__proto__是每个对象都有的一个属性,而prototype是函数才会有的属性。(通过Function.prototype.bind方法构造出来的函数是个例外,它没有prototype属性。)

[[Prototype]](隐式原型,非标准写法__proto__) 如果默认的 [[Get]] 操作不能直接在对象上找到被请求的属性,那么它会沿着对象的 [[Prototype]] 链 继续处理。 所有的对象(包括函数)都有一个私有属性(b.proto)指向它的原型对象(Foo.prototype)。该原型对象也有一个自己的原型对象(Object.proto) ,层层向上直到一个对象的原型对象为null。其中,下面3种隐式原型表述在本文中相同

  • Person.proto 非标准
  • Object.getPrototypeOf(Person) 标准
  • [[Prototype]] 概念

原型图解

1.首先了解内置对象

基本内置对象 String ,Number ,Boolean ,Object ,Function ,Array 其他内置对象 Date,RegExp,Error

  • 这些对象的[[Prototype]]都指向Function.prototype(Object.getPrototypeOf(String) === Function.prototype
  • Function.prototype是唯一一个typeof Xxx.prototype==='function'的prototype。其它的构造器的prototype都是’object’.
Object.prototype.constructor   === Object
Object.prototype.__proto__     === null

Function.prototype.constructor === Function
Function.prototype.__proto__   === Object.prototype

2.其次试着定义一个函数,看看发生什么

  • 定义一个函数(function Foo)会创建原型对象(Object Foo.prototype)。
  • constructor(构造器) 是在前面 用 new 关键字调用的任何函数。
function Foo(){}
Foo.__proto__             === Function.prototype

Foo.prototype.constructor === Foo//function Foo(){}
Foo.prototype.__proto__   === Object.prototype

3.接着new一下,也许应该叫委托

  • new会沿者 [[Prototype]] 链向上找到.constructor
  • new Foo() 得到一个新对象(我们叫他 a),这个新对象 a 内部地被 [[Prototype]] 链接至 Foo.prototype 对象。
var b = new Foo()//new作用:构建一个被链接到另一个对象的对象,外加这个函数要做的其他任何事。
b.__proto__===Foo.prototype

clipboard.png

4.instanceof 和原型链什么关系

object instanceof constructor用于测试constructor.prototype是否出现在object的原型链中的任何位置。

//像这样
object.__proto__===constructor.prototype?
object.__proto__.__proto__===constructor.prototype?
object.__proto__.__proto__.__proto__===constructor.prototype?
...
b instanceof Foo//true
b instanceof Object//true

Foo.prototype.isPrototypeOf(b)//true
Object.prototype.isPrototypeOf(b)//true

Foo.prototype = {};
b instanceof Foo//false
b instanceof Object//true
Foo.prototype.isPrototypeOf(b)//false
Object.prototype.isPrototypeOf(b)//true

‘类’与‘继承’

ES5

大家模拟了很多继承的方法,但本质上都是两种的变体:

1.原型继承

原型继承,本质是两个对象间建立链接,一个对象将对属性/函数的访问 委托 到另一个对象上。 1.1使用 new

// 假设有一个需要继承的一个类型 Animal
function Cat() {}
Cat.prototype = new Animal
// 添加一个属性
Cat.prototype.name = 'cat'

Bar.prototype = new Foo() 确实 创建了一个新的对象,这个新对象也的确链接到了我们希望的Foo.prototype。但是,它是用 Foo(…) “构造器调用”来这样做的。如果这个函数有任何副作用(比如logging,改变状态,注册其他对象,向 this添加数据属性,等等),这些副作用就会在链接时发生(而且很可能是对错误的对象!),而不是像可能希望的那样,仅最终在 Bar()的“后裔”被创建时发生。

于是,我们剩下的选择就是使用 Object.create(…) 来制造一个新对象,这个对象被正确地链接,而且没有调用 Foo(…)时所产生的副作用。一个轻微的缺点是,我们不得不创建新对象,并把旧的扔掉,而不是修改提供给我们的默认既存对象。

1.2使用Object.create()

function Shape() {this.x = 0;this.y = 0;}// 父类
Shape.prototype.move = function(x, y) {this.x += x;this.y += y;};// 父类的方法

function Rectangle() {Shape.call(this);} //子类

Rectangle.prototype = Object.create(Shape.prototype);// 子类续承父类
Rectangle.prototype.constructor = Rectangle;

var rect = new Rectangle();
  rect instanceof Rectangle; // true
  rect instanceof Shape; // true

Object.create()做了什么?

Object.create=function (o){
    function F() {}
    F.prototype = o;
    return new F();
}

两种操作将Bar.prototype 链接至 Foo.prototype:

// ES6 以前
// 扔掉默认既存的 `Bar.prototype`
Bar.prototype = Object.create( Foo.prototype );

// ES6+
// 修改既存的 `Bar.prototype`
Object.setPrototypeOf( Bar.prototype, Foo.prototype );

2.构造继承

构造继承,为了符合表面上我们用 new 调用它,而且我们观察到它“构建”了一个对象。 本质是因为 new 让函数调用变成了“构造器调用”

// 假设有一个需要继承的一个类型 Animal
function Cat(name){
  Animal.call(this)
  // 添加一个属性
  this.name = name || 'cat'
}

如何 检查“类”继承/自省:

错误方法a instanceof Foo instanceof 只能查询 a 的“祖先”。

**勉强正确方法:**用来检查 o1 是否关联到(委托至)o2 的帮助函数

function isRelatedTo(o1, o2) {
    function F(){}
    F.prototype = o2;
    return o1 instanceof F;
}

正确方法Foo.prototype.isPrototypeOf( a ) isPrototypeOf(…) 回答的问题是:在 a 的整个 [[Prototype]] 链中,Foo.prototype 出现过吗?

ES6

在 ES2015/ES6 中引入了class关键字,但只是语法糖,JavaScript 仍然是基于原型的。 注意:函数声明会提升,类声明不会。

//类声明
class Rectangle {
    constructor(height, width,x, y) {this.height = height,this.width = width,this.x = x,this.y = y;}
    get area() {return this.calcArea()}
    calcArea() {return this.height * this.width;}
    static distance(a, b) {return Math.hypot(a.x - b.x, a.y - b.y);}
}
const square1 = new Rectangle(10, 10,5,5);
const square2 = new Rectangle(10, 10,6,6);
console.log(square1.area);
console.log(Rectangle.distance(square1 , square2 ));

//类表达式
let Rectangle = class Rectangle {constructor() {}};

extend 实现继承

class Rectangle extends Shape {
  move() {
    super.move();//super 关键字用于调用对象的父对象上的函数
  }
}

常规(非可构造)对象 实现继承

var Animal = {};
class Cat {}
Object.setPrototypeOf(Cat.prototype, Animal);

混入(Mixin) Object.assign(target, …sources) 方法只会拷贝源对象(…sources)自身的并且可枚举的属性到目标对象(target)。

function MyClass() {
     SuperClass.call(this)
     OtherSuperClass.call(this)
}

// 继承一个类
MyClass.prototype = Object.create(SuperClass.prototype)
// 混合其它
Object.assign(MyClass.prototype, OtherSuperClass.prototype)
//Object.assign 会把  OtherSuperClass原型上的函数拷贝到 MyClass原型上,
//使 MyClass 的所有实例都可用 OtherSuperClass 的方法

// 重新指定constructor
MyClass.prototype.constructor = MyClass
MyClass.prototype.myMethod = function() {}// do a thing

当继承的函数被调用时,this 指向的是当前继承的对象,而不是继承的函数所在的原型对象。

var o = {
  a: 2,
  m: function(){
    return this.a + 1;
  }
};
var p = Object.create(o);
// p是一个继承自 o 的对象

p.a = 4; // 创建 p 的自身属性 a
console.log(p.m()); // 5

派生

class MyArray extends Array {
  static get [Symbol.species]() { return Array; }
}

面向委托的设计

var Widget = {
    init: function(width,height){
        this.width = width || 50;
        this.height = height || 50;
        this.$elem = null;
    },
    insert: function($where){
        if (this.$elem) {
            this.$elem.css( {
                width: this.width + "px",
                height: this.height + "px"
            } ).appendTo( $where );
        }
    }
};

var Button = Object.create( Widget );

Button.setup = function(width,height,label){
    // delegated call
    this.init( width, height );
    this.label = label || "Default";

    this.$elem = $( "<button>" ).text( this.label );
};
Button.build = function($where) {
    // delegated call
    this.insert( $where );
    this.$elem.click( this.onClick.bind( this ) );
};
Button.onClick = function(evt) {
    console.log( "Button '" + this.label + "' clicked!" );
};

$( document ).ready( function(){
    var $body = $( document.body );

    var btn1 = Object.create( Button );
    btn1.setup( 125, 30, "Hello" );

    var btn2 = Object.create( Button );
    btn2.setup( 150, 40, "World" );

    btn1.build( $body );
    btn2.build( $body );
} );

Object.create 和 new 区别 js中__proto__和prototype的区别和关系? 你不懂JS: this 与对象原型 行为委托

原文链接:https://segmentfault.com/a/1190000017288340

本站文章除注明转载外,均为本站原创或编译。欢迎任何形式的转载,但请务必注明出处。

转载请注明:文章转载自 JavaScript中文网 [https://www.javascriptcn.com]

本文地址:https://www.javascriptcn.com/read-47478.html

文章标题:FE.ES-理解ECMA Javascript的原型

相关文章
JavaScript编辑器推荐
主流编辑器有SublimeText,Notepad++,webstorm等,是使用最广泛的编辑器,但也有一些JavaScript编辑器提供有着各自的特性和功能,适应不同人的需求,以下是几款优秀的编辑器,相信你一定能找到自己喜欢的。 1. W...
2015-11-12
js性能优化 如何更快速加载你的JavaScript页面
确保代码尽量简洁 不要什么都依赖JavaScript。不要编写重复性的脚本。要把JavaScript当作糖果工具,只是起到美化作用。别给你的网站添加大量的JavaScript代码。只有必要的时候用一下。只有确实能改善用户体验的时候用一下。 ...
2015-11-12
10个强大的纯CSS3动画案例分享
我们的网页外观主要由CSS控制,编写CSS代码可以任意改变我们的网页布局以及网页内容的样式。CSS3的出现,更是可以让网页增添了不少动画元素,让我们的网页变得更加生动有趣,并且更易于交互。本文分享了10个非常炫酷的CSS3动画案例,希望大家...
2015-11-16
2015年JavaScript或“亲库而远框架”
2014年过去了,作为一个JavaScript开发者很难满怀信心的去“挽回”一个特定的库或技术,即便是强大的Angular,似乎也因为最近的一些事情而动摇。 2014年10月的ng-europe会议上,Angular开发者团队透露了一个关于...
2015-11-12
JavaScript实现PC手机端和嵌入式滑动拼图验证码三种效果
PC和手机端网站滑动拼图验证码效果源码,同时包涵了弹出式Demo,使用ajax形式提交二次验证码所需的验证结果值,嵌入式Demo,使用表单形式提交二次验证所需的验证结果值,移动端手动实现弹出式Demo三种效果 首先要确认前端使用页面,比如...
2017-03-17
JavaScript常用特效chm下载
下载地址:JavaScript常用特效chm下载 对了,如果打开空白,在手册上右键属性解除锁定即可。 ...
2015-11-12
css布局的各种FC简单介绍:BFC,IFC,GFC,FFC
什么是FC? Formatting Context,格式化上下文,指页面中一个渲染区域,拥有一套渲染规则,它决定了其子元素如何定位,以及与其他元素的相互关系和作用。 BFC 什么是BFC Block Formatting Context,块...
2018-05-17
从2014年的发展来展望JS的未来将会如何
&lt;font face=&quot;寰�杞�闆呴粦, Arial, sans-serif &quot;&gt;2014骞达紝杞�浠惰�屼笟鍙戝睍杩呴€燂紝鍚勭�嶈��瑷€灞傚嚭涓嶇┓锛屼互婊¤冻鐢ㄦ埛涓嶆柇鍙樺寲鐨勯渶姹傘€傝繖浜涜��...
2015-11-12
12个你未必知道的CSS小知识
虽然CSS并不是一种很复杂的技术,但就算你是一个使用CSS多年的高手,仍然会有很多CSS用法/属性/属性值你从来没使用过,甚至从来没听说过。 1.CSS的color属性并非只能用于文本显示 对于CSS的color属性,相信所有Web开发人员...
2015-11-12
ajax为什么令人惊异?ajax的优缺点
使用Ajax的最大优点,就是能在不更新整个页面的前提下维护数据。这使得Web应用程序更为迅捷地回应用户动作,并避免了在网络上发送那些没有改变的信息。 Ajax不需要任何浏览器插件,但需要用户允许JavaScript在浏览器上执行。就像DHT...
2015-11-12
回到顶部