名词剖析
字面量
对象字面量 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:false
与configurable: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
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 涌现过吗?
3.原型继续+组织函数继续
function A(name){ this.name=name; }
A.prototype.sayName=function(){ console.log(this.name); }
function B(age){ this.age=age; }
//原型继续
B.prototype=new A("mbj"); //被B的实例同享
var foo=new B(18);
foo.age; //18,age是本身照顾的属性
foo.name; //mbj,等价于foo.__proto__.name
foo.sayName(); //mbj,等价于foo.__proto__.proto__.sayName()
foo.toString(); //"[object Object]",等价于foo.__proto__.__proto__.__proto__.toString();
//组织函数继续
原型继续瑕玷:
1.一切子类同享父类实例,假如某一个子类修正了父类,其他的子类在继续的时刻,会形成意想不到的效果。
2.组织子类实例的时刻,不能给父类通报参数。
//组织函数继续,避免了原型继续瑕玷
function B(age,name){ this.age=age;A.call(this,name); }
var foo=new B(18,"wmy");
foo.name; //wmy
foo.age; //18
foo.sayName(); //undefined
组织函数继续瑕玷:
1.父类的prototype中的函数不能复用
//原型继续+组织函数继续
function B(age,name){ this.age=age;A.call(this,name); }
B.prototype=new A("mbj");
var foo=new B(18,"wmy");
foo.name; //wmy
foo.age; //18
foo.sayName(); //wmy
连系了上述两种体式格局的长处,但占用空间更大。
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 与对象原型
行动托付