深入理解Javascript面向对象编程,eval解析JSON字符

深刻掌握Javascript面向对象编程

2015/12/23 · JavaScript · 1 评论 · 面向对象

原稿出处: 涂根华   

豆蔻梢头:通晓构造函数原型(prototype)机制

prototype是javascript完结与治本持续的生龙活虎种体制,也是面向对象的设计观念.构造函数的原型存款和储蓄着引用对象的二个指南针,该指针指向与一个原型对象,对象内部存款和储蓄着函数的原始属性和措施;大家得以信任prototype属性,能够访谈原型内部的质量和艺术。

当构造函数被实列化后,全数的实例对象都得以访问构造函数的原型成员,假设在原型中宣称多个分子,全部的实列方法都足以分享它,比如如下代码:

JavaScript

// 构造函数A 它的原型有一个getName方法 function A(name){ this.name = name; } A.prototype.getName = function(){ return this.name; } // 实列化2次后 该2个实列皆有原型getName方法;如下代码 var instance1 = new A("longen1"); var instance2 = new A("longen2"); console.log(instance1.getName()); //longen1 console.log(instance2.getName()); // longen2

1
2
3
4
5
6
7
8
9
10
11
12
// 构造函数A 它的原型有一个getName方法
function A(name){
    this.name = name;
}
A.prototype.getName = function(){
    return this.name;
}
// 实列化2次后 该2个实列都有原型getName方法;如下代码
var instance1 = new A("longen1");
var instance2 = new A("longen2");
console.log(instance1.getName()); //longen1
console.log(instance2.getName()); // longen2

原型具有普通对象协会,能够将别的日常对象设置为原型对象; 日常意况下,对象都三番三回与Object,也得以明白Object是怀有目的的超类,Object是不曾原型的,而构造函数具备原型,由此实列化的对象也是Object的实列,如下代码:

JavaScript

// 实列化对象是构造函数的实列 console.log(instance1 instanceof A); //true console.log(instance2 instanceof A); // true // 实列化对象也是Object的实列 console.log(instance1 instanceof Object); //true console.log(instance2 instanceof Object); //true //Object 对象是具有指标的超类,因而构造函数也是Object的实列 console.log(A instanceof Object); // true // 不过实列化对象 不是Function对象的实列 如下代码 console.log(instance1 instanceof Function); // false console.log(instance2 instanceof Function); // false // 不过Object与Function有涉嫌 如下代码表达 console.log(Function instanceof Object); // true console.log(Object instanceof Function); // true

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 实列化对象是构造函数的实列
console.log(instance1 instanceof A); //true
console.log(instance2 instanceof A); // true
 
// 实列化对象也是Object的实列
console.log(instance1 instanceof Object); //true
console.log(instance2 instanceof Object); //true
 
//Object 对象是所有对象的超类,因此构造函数也是Object的实列
console.log(A instanceof Object); // true
 
// 但是实列化对象 不是Function对象的实列 如下代码
console.log(instance1 instanceof Function); // false
console.log(instance2 instanceof Function); // false
 
// 但是Object与Function有关系 如下代码说明
console.log(Function instanceof Object);  // true
console.log(Object instanceof Function);  // true

如上代码,Function是Object的实列,也得以是Object也是Function的实列;他们是2个例外的构造器,大家世襲看如下代码:

JavaScript

var f = new Function(); var o = new Object(); console.log("------------"); console.log(f instanceof Function); //true console.log(o instanceof Function); // false console.log(f instanceof Object); // true console.log(o instanceof Object); // true

1
2
3
4
5
6
7
var f = new Function();
var o = new Object();
console.log("------------");
console.log(f instanceof Function);  //true
console.log(o instanceof Function);  // false
console.log(f instanceof Object);    // true
console.log(o instanceof Object);   // true

大家领会,在原型上增添成员属性恐怕措施的话,它被有着的实列化对象所分享属性和艺术,但是倘诺实列化对象有和原型相近的积极分子成员名字的话,那么它取到的成员是本实列化对象,要是本实列对象中从不的话,那么它会到原型中去寻找该成员,借使原型找到就回到,不然的会重回undefined,如下代码测量检验

JavaScript

function B(){ this.name = "longen2"; } B.prototype.name = "AA"; B.prototype.getName = function(){ return this.name; }; var b1 = new B(); // 在本实列查找,找到就回来,不然到原型查找 console.log(b1.name); // longen2 // 在本实列未有找到该方法,就到原型去研究console.log(b1.getName());//longen2 // 如若在本实列未有找到的话,到原型上查找也从没找到的话,就重返undefined console.log(b1.a); // undefined // 以后本身动用delete运算符删除本地实列属性,那么取到的是正是原型属性了,如下代码: delete b1.name; console.log(b1.name); // AA

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function B(){
    this.name = "longen2";
}
B.prototype.name = "AA";
B.prototype.getName = function(){
    return this.name;
};
 
var b1 = new B();
// 在本实列查找,找到就返回,否则到原型查找
console.log(b1.name); // longen2
 
// 在本实列没有找到该方法,就到原型去查找
console.log(b1.getName());//longen2
 
// 如果在本实列没有找到的话,到原型上查找也没有找到的话,就返回undefined
console.log(b1.a); // undefined
 
// 现在我使用delete运算符删除本地实列属性,那么取到的是就是原型属性了,如下代码:
delete b1.name;
console.log(b1.name); // AA

二:明白原型域链的定义

原型的长处是能够以目的组织为载体,创立大气的实列,这么些实列能分享原型中的成员(属性和章程);相同的时候也足以运用原型实现面向对象中的世襲机制~ 如下代码:下边大家来看这几个布局函数AA和结构函数BB,当BB.prototype = new AA(11);奉行那个的时候,那么B就波澜起伏与A,B中的原型就有x的属性值为11

JavaScript

function AA(x){ this.x = x; } function BB(x) { this.x = x; } BB.prototype = new AA(11); console.log(BB.prototype.x); //11 // 大家再来驾驭原型世袭和原型链的概念,代码如下,都有注释 function A(x) { this.x = x; } // 在A的原型上定义两特性能x = 0 A.prototype.x = 0; function B(x) { this.x = x; } B.prototype = new A(1);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function AA(x){
    this.x = x;
}
function BB(x) {
    this.x = x;
}
BB.prototype = new AA(11);
console.log(BB.prototype.x); //11
 
// 我们再来理解原型继承和原型链的概念,代码如下,都有注释
function A(x) {
    this.x = x;
}
// 在A的原型上定义一个属性x = 0
A.prototype.x = 0;
function B(x) {
    this.x = x;
}
B.prototype = new A(1);

实列化A new A(1)的时候 在A函数内this.x =1, B.prototype = new A(1);B.prototype 是A的实列 也正是B继承于A, 即B.prototype.x = 1;  如下代码:

JavaScript

console.log(B.prototype.x); // 1 // 定义C的构造函数 function C(x) { this.x = x; } C.prototype = new B(2);

1
2
3
4
5
6
console.log(B.prototype.x); // 1
// 定义C的构造函数
function C(x) {
    this.x = x;
}
C.prototype = new B(2);

C.prototype = new B(2); 也正是C.prototype 是B的实列,C世袭于B;那么new B(2)的时候 在B的构造函数内 this.x = 2;那么 C的原型上会有几天性能x =2 即C.prototype.x = 2; 如下代码:

JavaScript

console.log(C.prototype.x); // 2

1
console.log(C.prototype.x); // 2

下边是实列化 var d = new C(3); 实列化C的构造函数时候,那么在C的构造函数内this.x = 3; 由此如下打字与印刷实列化后的d.x = 3;如下代码:

JavaScript

var d = new C(3); console.log(d.x); // 3

1
2
var d = new C(3);
console.log(d.x); // 3

去除d.x 再拜谒d.x的时候 本实列对象被删掉,只可以从原型上去搜索;由于C.prototype = new B(2); 也正是C世襲于B,因而C的原型也可以有x = 2;即C.prototype.x = 2; 如下代码:

JavaScript

delete d.x; console.log(d.x); //2

1
2
delete d.x;
console.log(d.x);  //2

删除C.prototype.x后,大家从上面代码知道,C是继续于B的,自个儿的原型被删掉后,会去搜索父成分的原型链,因而在B的原型上找到x =1; 如下代码:

JavaScript

delete C.prototype.x; console.log(d.x); // 1

1
2
delete C.prototype.x;
console.log(d.x);  // 1

当删除B的原型属性x后,由于B是三番五回于A的,因而会从父成分的原型链上查找A原型上是不是有x的性质,假若有的话,就回来,不然看A是不是有三番两回,未有持续的话,继续往Object上去搜索,若无找到就重临undefined 因而当删除B的原型x后,delete B.prototype.x; 打字与印刷出A上的原型x=0; 如下代码:

JavaScript

delete B.prototype.x; console.log(d.x); // 0 // 继续删除A的原型x后 结果未有找到,就重临undefined了; delete A.prototype.x; console.log(d.x); // undefined

1
2
3
4
5
6
delete B.prototype.x;
console.log(d.x);  // 0
 
// 继续删除A的原型x后 结果没有找到,就返回undefined了;
delete A.prototype.x;
console.log(d.x);  // undefined

在javascript中,一切都以对象,Function和Object都以函数的实列;构造函数的父原型指向于Function原型,Function.prototype的父原型指向与Object的原型,Object的父原型也本着与Function原型,Object.prototype是具有原型的顶层;

正如代码:

JavaScript

Function.prototype.a = function(){ console.log("笔者是父原型Function"); } Object.prototype.a = function(){ console.log("笔者是 父原型Object"); } function A(){ this.a = "a"; } A.prototype = { B: function(){ console.log("b"); } } // Function 和 Object都是函数的实列 如下: console.log(A instanceof Function); // true console.log(A instanceof Object); // true // A.prototype是四个指标,它是Object的实列,但不是Function的实列 console.log(A.prototype instanceof Function); // false console.log(A.prototype instanceof Object); // true // Function是Object的实列 同是Object也是Function的实列 console.log(Function instanceof Object); // true console.log(Object instanceof Function); // true /* * Function.prototype是Object的实列 但是Object.prototype不是Function的实列 * 表达Object.prototype是具备父原型的顶层 */ console.log(Function.prototype instanceof Object); //true console.log(Object.prototype instanceof Function); // false

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
Function.prototype.a = function(){
    console.log("我是父原型Function");
}
Object.prototype.a = function(){
    console.log("我是 父原型Object");
}
function A(){
    this.a = "a";
}
A.prototype = {
    B: function(){
        console.log("b");
    }
}
// Function 和 Object都是函数的实列 如下:
console.log(A instanceof Function);  // true
console.log(A instanceof Object); // true
 
// A.prototype是一个对象,它是Object的实列,但不是Function的实列
console.log(A.prototype instanceof Function); // false
console.log(A.prototype instanceof Object); // true
 
// Function是Object的实列 同是Object也是Function的实列
console.log(Function instanceof Object);   // true
console.log(Object instanceof Function); // true
 
/*
* Function.prototype是Object的实列 但是Object.prototype不是Function的实列
* 说明Object.prototype是所有父原型的顶层
*/
console.log(Function.prototype instanceof Object);  //true
console.log(Object.prototype instanceof Function);  // false

三:掌握原型继承机制

构造函数都有叁个指南针指向原型,Object.prototype是有着原型对象的顶层,举个例子如下代码:

JavaScript

var obj = {}; Object.prototype.name = "tugenhua"; console.log(obj.name); // tugenhua

1
2
3
var obj = {};
Object.prototype.name = "tugenhua";
console.log(obj.name); // tugenhua

给Object.prototype 定义叁性格质,通过字面量构建的指标的话,都会从父类那边获得Object.prototype的性质;

从上边代码大家知晓,原型世襲的艺术是:假设A必要继续于B,那么A.prototype(A的原型) = new B()(作为B的实列卡塔 尔(英语:State of Qatar) 就可以完毕A继承于B; 因而我们上面能够开头化三个空的构造函数;然后把目的赋值给构造函数的原型,然后重回该构造函数的实列; 就能够达成接二连三; 如下代码:

JavaScript

if(typeof Object.create !== 'function') { Object.create = function(o) { var F = new Function(); F.prototype = o; return new F(); } } var a = { name: 'longen', getName: function(){ return this.name; } }; var b = {}; b = Object.create(a); console.log(typeof b); //object console.log(b.name); // longen console.log(b.getName()); // longen

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
if(typeof Object.create !== 'function') {
    Object.create = function(o) {
        var F = new Function();
        F.prototype = o;
        return new F();
    }
}
var a = {
    name: 'longen',
    getName: function(){
        return this.name;
    }
};
var b = {};
b = Object.create(a);
console.log(typeof b); //object
console.log(b.name);   // longen
console.log(b.getName()); // longen

如上代码:大家先检查测试Object是还是不是已经有Object.create该办法;若无的话就创建二个; 该措施内制造多少个空的构造器,把参数对象传递给构造函数的原型,最终回到该构造函数的实列,就完成了持续情势;如上测量试验代码:先定义三个a目的,有成员属性name=’longen’,还恐怕有三个getName()方法;最终回来该name属性; 然后定义叁个b空对象,使用Object.create(a);把a对象世袭给b对象,由此b对象也可能有品质name和成员方法getName();

 掌握原型查找原理:指标查找先在该构造函数内找出对应的性质,假诺该指标未有该属性的话,

那便是说javascript会试着从该原型上去搜索,假如原型对象中也未曾该属性的话,那么它们会从原型中的原型去搜寻,直到查找的Object.prototype也不曾该属性的话,那么就能够再次回到undefined;由此大家想要仅在该对象内搜索的话,为了加强质量,大家得以应用hasOwnProperty()来判别该对象内有未有该属性,要是有的话,就实践代码(使用for-in循环查找):如下:

JavaScript

var obj = { "name":'tugenhua', "age":'28' }; // 使用for-in循环 for(var i in obj) { if(obj.hasOwnProperty(i)) { console.log(obj[i]); //tugenhua 28 } }

1
2
3
4
5
6
7
8
9
10
var obj = {
    "name":'tugenhua',
    "age":'28'
};
// 使用for-in循环
for(var i in obj) {
    if(obj.hasOwnProperty(i)) {
        console.log(obj[i]); //tugenhua 28
    }
}

如上采纳for-in循环查找对象里面包车型客车属性,不过大家需求知道的是:for-in循环查找对象的质量,它是不保证顺序的,for-in循环和for循环;最本色的分别是:for循环是有各类的,for-in循环遍历对象是冬天的,因而大家假如须求对象保证顺序的话,能够把目的转变为数组来,然后再利用for循环遍历就可以;

上面我们来斟酌原型世襲的优点和破绽

JavaScript

// 先看上面包车型地铁代码: // 定义构造函数A,定义特权属性和特权方法 function A(x) { this.x1 = x; this.getX1 = function(){ return this.x1; } } // 定义构造函数B,定义特权属性和特权方法 function B(x) { this.x2 = x; this.getX2 = function(){ return this.x1 + this.x2; } } B.prototype = new A(1);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 先看下面的代码:
// 定义构造函数A,定义特权属性和特权方法
function A(x) {
    this.x1 = x;
    this.getX1 = function(){
        return this.x1;
    }
}
// 定义构造函数B,定义特权属性和特权方法
function B(x) {
    this.x2 = x;
    this.getX2 = function(){
        return this.x1 + this.x2;
    }
}
B.prototype = new A(1);

B.prototype = new A(1);那句代码推行的时候,B的原型世袭于A,因而B.prototype也可能有A的特性和措施,即:B.prototype.x1 = 1; B.prototype.getX1 方法;然而B也可以有谐和的特权属性x2和特权方法getX2; 如下代码:

JavaScript

function C(x) { this.x3 = x; this.getX3 = function(){ return this.x3 + this.x2; } } C.prototype = new B(2); C.prototype = new B(2);这句代码施行的时候,C的原型世襲于B,因而C.prototype.x2 = 2; C.prototype.getX2方法且C也是有温馨的特权属性x3和特权方法getX3, var b = new B(2); var c = new C(3); console.log(b.x1); // 1 console.log(c.x1); // 1 console.log(c.getX3()); // 5 console.log(c.getX2()); // 3 var b = new B(2);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function C(x) {
    this.x3 = x;
    this.getX3 = function(){
        return this.x3 + this.x2;
    }
}
C.prototype = new B(2);
C.prototype = new B(2);这句代码执行的时候,C的原型继承于B,因此C.prototype.x2 = 2; C.prototype.getX2方法且C也有自己的特权属性x3和特权方法getX3,
var b = new B(2);
var c = new C(3);
console.log(b.x1);  // 1
console.log(c.x1);  // 1
console.log(c.getX3()); // 5
console.log(c.getX2()); // 3
var b = new B(2);

实列化B的时候 b.x1 首先会在构造函数内查找x1属性,未有找到,由于B的原型世袭于A,因而A有x1属性,由此B.prototype.x1 = 1找到了;var c = new C(3); 实列化C的时候,从地方的代码能够见见C世襲于B,B世袭于A,因而在C函数中未有找到x1属性,会往原型继续找出,直到找到父成分A有x1属性,由此c.x1 = 1;c.getX3()方法; 再次来到this.x3+this.x2 this.x3 = 3;this.x2 是B的性质,因而this.x2 = 2;c.getX2(); 查找的办法也同等,不再解释

prototype的久治不愈的病痛与亮点如下:

可取是:能够允许三个对象实列分享原型对象的成员及方法,

瑕疵是:1. 各样构造函数独有二个原型,由此不直接扶植多种世襲;

2. 不可能很好地支撑多参数或动态参数的父类。在原型继承阶段,客户还不可能决定以

哪些参数来实列化构造函数。

四:掌握使用类世袭(世襲的越来越好的方案)

类世袭也称之为构造函数世袭,在子类中施行父类的构造函数;完结原理是:能够将贰个结构函数A的情势赋值给另贰个构造函数B,然后调用该办法,使组织函数A在组织函数B内部被实施,这个时候构造函数B就具备了社团函数A中的属性和措施,那便是接收类世袭达成B世袭与A的基本原理;

平时来讲代码完结demo:

JavaScript

function A(x) { this.x = x; this.say = function(){ return this.x; } } function B(x,y) { this.m = A; // 把社团函数A作为三个常常性函数引用给有的时候措施m this.m(x); // 实施组织函数A; delete this.m; // 肃清有时措施this.m this.y = y; this.method = function(){ return this.y; } } var a = new A(1); var b = new B(2,3); console.log(a.say()); //输出1, 实施组织函数A中的say方法 console.log(b.say()); //输出2, 能实行该方式求证被一而再了A中的方法 console.log(b.method()); // 输出3, 构造函数也保有本人的办法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function A(x) {
    this.x = x;
    this.say = function(){
        return this.x;
    }
}
function B(x,y) {
    this.m = A; // 把构造函数A作为一个普通函数引用给临时方法m
    this.m(x);  // 执行构造函数A;
    delete this.m; // 清除临时方法this.m
    this.y = y;
    this.method = function(){
        return this.y;
    }
}
var a = new A(1);
var b = new B(2,3);
console.log(a.say()); //输出1, 执行构造函数A中的say方法
console.log(b.say()); //输出2, 能执行该方法说明被继承了A中的方法
console.log(b.method()); // 输出3, 构造函数也拥有自己的方法

地点的代码实现了简要的类世袭的底工,不过在纷纷的编制程序中是不会采取方面包车型客车情势的,因为上边包车型地铁代码相当不足严厉;代码的耦合性高;大家得以选择越来越好的法子如下:

JavaScript

function A(x) { this.x = x; } A.prototype.getX = function(){ return this.x; } // 实例化A var a = new A(1); console.log(a.x); // 1 console.log(a.getX()); // 输出1 // 现行反革命我们来成立构造函数B,让其B世襲与A,如下代码: function B(x,y) { this.y = y; A.call(this,x); } B.prototype = new A(); // 原型继承console.log(B.prototype.constructor); // 输出构造函数A,指针指向与构造函数A B.prototype.constructor = B; // 重新安装构造函数,使之指向B console.log(B.prototype.constructor); // 指向构造函数B B.prototype.getY = function(){ return this.y; } var b = new B(1,2); console.log(b.x); // 1 console.log(b.getX()); // 1 console.log(b.getY()); // 2 // 下边是以身作则对构造函数getX举办重写的办法如下: B.prototype.getX = function(){ return this.x; } var b2 = new B(10,20); console.log(b2.getX()); // 输出10

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
function A(x) {
    this.x = x;
}
A.prototype.getX = function(){
    return this.x;
}
// 实例化A
var a = new A(1);
console.log(a.x); // 1
console.log(a.getX()); // 输出1
// 现在我们来创建构造函数B,让其B继承与A,如下代码:
function B(x,y) {
    this.y = y;
    A.call(this,x);
}
B.prototype = new A();  // 原型继承
console.log(B.prototype.constructor); // 输出构造函数A,指针指向与构造函数A
B.prototype.constructor = B;          // 重新设置构造函数,使之指向B
console.log(B.prototype.constructor); // 指向构造函数B
B.prototype.getY = function(){
    return this.y;
}
var b = new B(1,2);
console.log(b.x); // 1
console.log(b.getX()); // 1
console.log(b.getY()); // 2
 
// 下面是演示对构造函数getX进行重写的方法如下:
B.prototype.getX = function(){
    return this.x;
}
var b2 = new B(10,20);
console.log(b2.getX());  // 输出10

上边大家来解析上面包车型客车代码:

在构造函数B内,使用A.call(this,x);那句代码的意义是:我们都了解使用call或然apply方法能够修改this指针指向,进而能够完成类的三回九转,因而在B构造函数内,把x的参数字传送递给A构造函数,并且继续于结构函数A中的属性和艺术;

动用那句代码:B.prototype = new A();  能够兑现原型世袭,约等于B能够世袭A中的原型全体的方法;console.log(B.prototype.constructor); 打印出输出构造函数A,指针指向与组织函数A;大家掌握的是,当定义构造函数时候,其原型对象暗中同意是二个Object类型的叁个实例,其布局器暗中认可会被设置为构造函数自身,若是改动构造函数prototype属性值,使其针对性于另三个对象的话,那么新对象就不会有着原来的constructor的值,举例第一回打字与印刷console.log(B.prototype.constructor); 指向于被实例化后的构造函数A,重写设置B的constructor的属性值的时候,第二回打字与印刷就照准于自家B;由此B世袭与构造A及其原型的全部属性和措施,当然我们也能够对构造函数B重写构造函数A中的方法,如上面最终几句代码是对组织函数A中的getX方法开展重写,来落实团结的事务~;

五:建议使用封装类完毕三番两次

封装类达成接二连三的基本原理:先定义二个封装函数extend;该函数有2个参数,Sub代表子类,Sup代表超类;在函数内,先定义三个空函数F, 用来落时效益中转,先设置F的原型为超类的原型,然后把空函数的实例传递给子类的原型,使用叁个空函数的功利是:幸免直接实例化超类大概会带动系统性格难题,例如超类的实例十分大的话,实例化会占用超级多内部存款和储蓄器;

正如代码:

JavaScript

function extend(Sub,Sup) { //Sub代表子类,Sup代表超类 // 首先定义三个空函数 var F = function(){}; // 设置空函数的原型为超类的原型 F.prototype = Sup.prototype; // 实例化空函数,并把超类原型引用传递给子类 Sub.prototype = new F(); // 重新恢复生机设置子类原型的构造器为子类自个儿Sub.prototype.constructor = Sub; // 在子类中保留超类的原型,制止子类与超类耦合 Sub.sup = Sup.prototype; if(Sup.prototype.constructor === Object.prototype.constructor) { // 检查测验超类原型的构造器是或不是为原型本人 Sup.prototype.constructor = Sup; } } 测量检验代码如下: // 下边大家定义2个类A和类B,大家指标是落实B世袭于A function A(x) { this.x = x; this.getX = function(){ return this.x; } } A.prototype.add = function(){ return this.x + this.x; } A.prototype.mul = function(){ return this.x * this.x; } // 构造函数B function B(x){ A.call(this,x); // 世袭构造函数A中的全数属性及形式 } extend(B,A); // B世袭于A var b = new B(11); console.log(b.getX()); // 11 console.log(b.add()); // 22 console.log(b.mul()); // 121

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
function extend(Sub,Sup) {
    //Sub表示子类,Sup表示超类
    // 首先定义一个空函数
    var F = function(){};
 
    // 设置空函数的原型为超类的原型
    F.prototype = Sup.prototype;
 
// 实例化空函数,并把超类原型引用传递给子类
    Sub.prototype = new F();
 
    // 重置子类原型的构造器为子类自身
    Sub.prototype.constructor = Sub;
 
    // 在子类中保存超类的原型,避免子类与超类耦合
    Sub.sup = Sup.prototype;
 
    if(Sup.prototype.constructor === Object.prototype.constructor) {
        // 检测超类原型的构造器是否为原型自身
        Sup.prototype.constructor = Sup;
    }
 
}
测试代码如下:
// 下面我们定义2个类A和类B,我们目的是实现B继承于A
function A(x) {
    this.x = x;
    this.getX = function(){
        return this.x;
    }
}
A.prototype.add = function(){
    return this.x + this.x;
}
A.prototype.mul = function(){
    return this.x * this.x;
}
// 构造函数B
function B(x){
    A.call(this,x); // 继承构造函数A中的所有属性及方法
}
extend(B,A);  // B继承于A
var b = new B(11);
console.log(b.getX()); // 11
console.log(b.add());  // 22
console.log(b.mul());  // 121

注意:在封装函数中,有那样一句代码:Sub.sup = Sup.prototype; 大家现在能够来明白下它的含义:

例如在B世襲与A后,作者给B函数的原型再定义二个与A相符的原型相通的方法add();

如下代码

JavaScript

extend(B,A); // B继承于A var b = new B(11); B.prototype.add = function(){ return this.x + "" + this.x; } console.log(b.add()); // 1111

1
2
3
4
5
6
extend(B,A);  // B继承于A
var b = new B(11);
B.prototype.add = function(){
    return this.x + "" + this.x;
}
console.log(b.add()); // 1111

这就是说B函数中的add方法会覆盖A函数中的add方法;因而为了不隐瞒A类中的add()方法,且调用A函数中的add方法;可以如下编写代码:

JavaScript

B.prototype.add = function(){ //return this.x + "" + this.x; return B.sup.add.call(this); } console.log(b.add()); // 22

1
2
3
4
5
B.prototype.add = function(){
    //return this.x + "" + this.x;
    return B.sup.add.call(this);
}
console.log(b.add()); // 22

B.sup.add.call(this); 中的B.sup就包含了结构函数A函数的指针,由此包含A函数的兼具属性和形式;由此得以调用A函数中的add方法;

如上是完成一连的二种方法,类世袭和原型世袭,不过那些后续不也许继续DOM对象,也不扶持世袭系统静态对象,静态方法等;譬如Date对象如下:

JavaScript

// 使用类世袭Date对象 function D(){ Date.apply(this,arguments); // 调用Date对象,对其引述,完毕世襲 } var d = new D(); console.log(d.toLocaleString()); // [object object]

1
2
3
4
5
6
// 使用类继承Date对象
function D(){
    Date.apply(this,arguments); // 调用Date对象,对其引用,实现继承
}
var d = new D();
console.log(d.toLocaleString()); // [object object]

如上代码运转打字与印刷出object,大家能够看出采取类世袭不能够兑现系统静态方法date对象的再而三,因为她不是粗略的函数结构,对表明,赋值和起初化都实行了包装,由此不可能继续;

下边我们再来看看使用原型世襲date对象;

JavaScript

function D(){} D.prototype = new D(); var d = new D(); console.log(d.toLocaleString());//[object object]

1
2
3
4
function D(){}
D.prototype = new D();
var d = new D();
console.log(d.toLocaleString());//[object object]

我们从代码中看看,使用原型世襲也敬敏不谢继续Date静态方法;不过大家能够如下封装代码世襲:

JavaScript

function D(){ var d = new Date(); // 实例化Date对象 d.get = function(){ // 定义本地点法,直接调用Date对象的措施 console.log(d.toLocaleString()); } return d; } var d = new D(); d.get(); // 二〇一五/12/21 早上12:08:38

1
2
3
4
5
6
7
8
9
function D(){
    var d = new Date();  // 实例化Date对象
    d.get = function(){ // 定义本地方法,间接调用Date对象的方法
        console.log(d.toLocaleString());
    }
    return d;
}
var d = new D();
d.get(); // 2015/12/21 上午12:08:38

六:明白使用复制世襲

复制继承的基本原理是:先规划一个空对象,然后选拔for-in循环来遍历对象的成员,将该对象的成员叁个一个复制给新的空对象里面;那样就兑现了复制世袭了;如下代码:

JavaScript

function A(x,y) { this.x = x; this.y = y; this.add = function(){ return this.x + this.y; } } A.prototype.mul = function(){ return this.x * this.y; } var a = new A(2,3); var obj = {}; for(var i in a) { obj[i] = a[i]; } console.log(obj); // object console.log(obj.x); // 2 console.log(obj.y); // 3 console.log(obj.add()); // 5 console.log(obj.mul()); // 6

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function A(x,y) {
    this.x = x;
    this.y = y;
    this.add = function(){
        return this.x + this.y;
    }
}
A.prototype.mul = function(){
    return this.x * this.y;
}
var a = new A(2,3);
var obj = {};
for(var i in a) {
    obj[i] = a[i];
}
console.log(obj); // object
console.log(obj.x); // 2
console.log(obj.y); // 3
console.log(obj.add()); // 5
console.log(obj.mul()); // 6

如上代码:先定义四个构造函数A,函数里面有2个属性x,y,还会有八个add方法,该构造函数原型有七个mul方法,首先实列化下A后,再次创下立一个空对象obj,遍历对象多少个个复制给空对象obj,从地点的打字与印刷效果来看,大家可以见到曾经落到实处了复制世襲了;对于复制世袭,大家能够封装成如下方法来调用:

JavaScript

// 为Function扩充复制世襲方法 Function.prototype.extend = function(o) { for(var i in o) { //把参数对象的成员复制给当下目的的构造函数原型对象 this.constructor.prototype[i] = o[i]; } } // 测量试验代码如下: var o = function(){}; o.extend(new A(1,2)); console.log(o.x); // 1 console.log(o.y); // 2 console.log(o.add()); // 3 console.log(o.mul()); // 2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 为Function扩展复制继承方法
Function.prototype.extend = function(o) {
    for(var i in o) {
        //把参数对象的成员复制给当前对象的构造函数原型对象
        this.constructor.prototype[i] = o[i];
    }
}
// 测试代码如下:
var o = function(){};
o.extend(new A(1,2));
console.log(o.x);  // 1
console.log(o.y);  // 2
console.log(o.add()); // 3
console.log(o.mul()); // 2

地方封装的扩张世襲方法中的this对象指向于近日实列化后的靶子,并非指向于构造函数本人,由此要使用原型增加成员来讲,就要求利用constructor属性来指向它的构造器,然后经过prototype属性指向构造函数的原型;

复制世襲犹如下优点:

1. 它不可能三番两次系统宗旨对象的只读方法和质量

2. 假如指标数据丰硕多以来,那样叁个个复制的话,质量是超级低的;

3. 独有对象被实列化后,本事给遍历对象的分子和天性,相对来讲远远不够灵活;

4. 复制世襲只是简短的赋值,所以若是赋值的指标是援用类型的目的的话,或然会设有一点副成效;如上我们看看有如上部分劣点,上面大家能够应用clone(克隆的办法)来优化下:

基本思路是:为Function扩充二个方法,该方式能够把参数对象赋值赋值多少个空构造函数的原型对象,然后实列化构造函数并回到实列对象,这样该目的就全部了该指标的有着成员;代码如下:

JavaScript

Function.prototype.clone = function(o){ function Temp(){}; Temp.prototype = o; return Temp(); } // 测量检验代码如下: Function.clone(new A(1,2)); console.log(o.x); // 1 console.log(o.y); // 2 console.log(o.add()); // 3 console.log(o.mul()); // 2

1
2
3
4
5
6
7
8
9
10
11
Function.prototype.clone = function(o){
    function Temp(){};
    Temp.prototype = o;
    return Temp();
}
// 测试代码如下:
Function.clone(new A(1,2));
console.log(o.x);  // 1
console.log(o.y);  // 2
console.log(o.add()); // 3
console.log(o.mul()); // 2

2 赞 19 收藏 1 评论

图片 1

eval分析JSON字符串的二个小意思

2016/02/24 · JavaScript · JSON

原来的书文出处: 韩子迟   

以前写过豆蔻梢头篇 关于 JSON 的牵线著作,里面聊到了 JSON 的剖判。大家都精晓,高端浏览器能够用  JSON.parse() API 将贰个 JSON 字符串解析成 JSON 数据,稍稍欠妥点的做法,咱们能够用 eval() 函数。

JavaScript

var str = '{"name": "hanzichi", "age": 10}'; var obj = eval('(' + str + ')'); console.log(obj); // Object {name: "hanzichi", age: 10}

1
2
3
var str = '{"name": "hanzichi", "age": 10}';
var obj = eval('(' + str + ')');
console.log(obj); // Object {name: "hanzichi", age: 10}

 

是或不是注意到,向 eval() 传参时,str 变量外裹了意气风发层小括号?为啥要那样做?

大家先来探视 eval 函数的定义以致接收。

eval() 的参数是二个字符串。假若字符串表示了四个表达式,eval() 会对表明式求值。假诺参数表示了一个或多个 JavaScript 注解, 那么 eval() 会实施注脚。不要调用 eval() 来为算数表达式求值; JavaScript 会自动为算数表明式求值。

简言之地说,eval 函数的参数是叁个字符串,假使把字符串 “noString” 化管理,那么获得的将是健康的可以运营的 JavaScript 语句。

怎么说?举个栗子,如下代码:

JavaScript

var str = "alert('hello world')"; eval(str);

1
2
var str = "alert('hello world')";
eval(str);

执行后弹出 “hello world”。大家把 str 变量 “noString” 化,残暴点的做法便是去掉外面的引号,内部调节(转义等卡塔 尔(阿拉伯语:قطر‎,然后就改为了:

JavaScript

alert('hello world')

1
alert('hello world')

very good!那是正常的能够运作的 JavaScript 语句!运营之!

再回去起头的主题素材,为何 JSON 字符串要裹上小括号。假使不加,是其同样子的:

JavaScript

var str = '{"name": "hanzichi", "age": 10}'; var obj = eval(str); // Uncaught SyntaxError: Unexpected token :

1
2
var str = '{"name": "hanzichi", "age": 10}';
var obj = eval(str);  // Uncaught SyntaxError: Unexpected token :

恩,报错了。为何会报错?试试把 str “noString” 化,实行一下:

JavaScript

{"name": "hanzichi", "age": 10}; // Uncaught SyntaxError: Unexpected token :

1
{"name": "hanzichi", "age": 10};  // Uncaught SyntaxError: Unexpected token :

必然,一个 JSON 对象可能说是二个指标根本就不是能执行的 JavaScript 语句!等等,试试以下代码:

JavaScript

var str = '{name: "hanzichi"}'; var obj = eval(str); console.log(obj); // hanzichi

1
2
3
var str = '{name: "hanzichi"}';
var obj = eval(str);
console.log(obj); // hanzichi

那又是什么样鬼?但是给 name 加上 “” 又报错?

JavaScript

var str = '{"name": "hanzichi"}'; var obj = eval(str); // Uncaught SyntaxError: Unexpected token : console.log(obj);

1
2
3
var str = '{"name": "hanzichi"}';
var obj = eval(str);  // Uncaught SyntaxError: Unexpected token :
console.log(obj);

好呢,快晕了,其实还可以够将 str “nostring” 化,看看是还是不是能科学施行的 JavaScript 语句。前者的结果是:

JavaScript

{name: "hanzichi"}

1
{name: "hanzichi"}

这诚然是一条官方的 JavaScript 语句。{} 我们不光能在 if、for 语句等景观使用,以致可以在别的时候,因为 ES6 早前 JavaScript 独有块级作用域,所以对于成效域什么的并不会有哪些矛盾。去掉 {} 后 name: "hanzichi"也是法定的话语,一个 label 语句,label 语句在跳出嵌套的大循环中丰富好用,具体能够参谋 label,而作为 label 语句的号子,name 是不能够带引号的,标志能放在 JavaScript 代码的其它岗位,用不到也没提到。

若果三个目的有了八个 key,举个例子 {name: "hanzichi", age: 10} ,ok,多少个 label 语句?将 “hanzhichi” 以致 10 分别作为是言语,但是 讲话之间只好用封号连接!(表达式之间能力用逗号卡塔尔。所以改成下边那样也是尚未难题的:

JavaScript

var str = '{name: "hanzichi"; age: 10}'; var obj = eval(str); console.log(obj); // 10

1
2
3
var str = '{name: "hanzichi"; age: 10}';
var obj = eval(str);  
console.log(obj); // 10

越扯越远,随笔初步代码的怪诞的来由是找到了,为啥套个括号就会缓和呢?一句话来讲,() 会把语句调换来表明式,称为语句表明式。括号里的代码都会被转变为表明式求值并且重返,对象字面量必需作为表明式而留存

正文并不会大谈表达式,关于表明式,能够参照文末链接。值得铭记的一点是,表明式永久有三个再次来到值。超越贰分之一表明式会卷入在() 内,小括号内无法为空,若是有多个表明式,用逗号隔断,也正是所谓的逗号表明式,会回去最终四个的值。

谈到表明式,不能不提函数表明式,早前翻译过黄金时代篇有关马上实行函数表明式的稿子,能够参谋下,文末。

Read More:

  • [译]JavaScript中:表明式和语句的界别
  • (译卡塔 尔(英语:State of Qatar)详细明白javascript立时实行函数表明式(IIFE卡塔 尔(阿拉伯语:قطر‎
  • 深远商讨javascript的 {} 语句块

    1 赞 1 收藏 评论

图片 2

同理可得黄金年代招完结json数据可视化

2015/07/21 · JavaScript · JSON, 数量可视化

原稿出处: 吕大豹   

付出一个里边职能时遇到的急需,要把json数据在页面上展现出来,平日浏览器会安装jsonView那样的扩张来看json数据,可是程序要用到的话该咋办呢?前些天在互连网搜索的时候,开采了那些小手艺,分享一下。

要用到的宗旨是JSON.stringify这一个函数,没悟出吧,日常我们只把它用来种类号json数据。然而那些stringify是有五个参数的,

JavaScript

JSON.stringify(value [, replacer] [, space])

1
JSON.stringify(value [, replacer] [, space])

,具体描述请看这里:

笔者们要用到的正是那第多少个参数,它能够内定在变化的字符串中增添少空格,进而生成有早晚格式的字符串。生成的字符串大家得以献身<pre>标签中,这样就会很好的体现缩进。然后呢,为了让变化的多稀少高亮效果,大家还是能够写三个简短的高亮函数。基本正是这么个原理啦,请看代码达成:

JavaScript

function output(inp) { document.body.appendChild(document.createElement('pre')).innerHTML = inp; } function syntaxHighlight(json) { json = json.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>'); return json.replace(/("(\u[a-zA-Z0-9]{4}|\[^u]|[^\"])*"(s*:)?|b(true|false|null)b|-?d+(?:.d*)?(?:[eE][+-]?d+)?)/g, function (match) { var cls = 'number'; if (/^"/.test(match)) { if (/:$/.test(match)) { cls = 'key'; } else { cls = 'string'; } } else if (/true|false/.test(match)) { cls = 'boolean'; } else if (/null/.test(match)) { cls = 'null'; } return '<span class="' + cls + '">' + match + '</span>'; }); } var obj = { num: 1234, str: '字符串', arr: [1,2,3,4,5,6], obj: { name: 'tom', age: 10, like: ['a', 'b'] } }; var str = JSON.stringify(obj, undefined, 4); output(syntaxHighlight(str));

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
function output(inp) {
    document.body.appendChild(document.createElement('pre')).innerHTML = inp;
}
 
function syntaxHighlight(json) {
    json = json.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
    return json.replace(/("(\u[a-zA-Z0-9]{4}|\[^u]|[^\"])*"(s*:)?|b(true|false|null)b|-?d+(?:.d*)?(?:[eE][+-]?d+)?)/g, function (match) {
        var cls = 'number';
        if (/^"/.test(match)) {
            if (/:$/.test(match)) {
                cls = 'key';
            } else {
                cls = 'string';
            }
        } else if (/true|false/.test(match)) {
            cls = 'boolean';
        } else if (/null/.test(match)) {
            cls = 'null';
        }
        return '<span class="' + cls + '">' + match + '</span>';
    });
}
 
var obj = {
    num: 1234,
    str: '字符串',
    arr: [1,2,3,4,5,6],
    obj: {
        name: 'tom',
        age: 10,
        like: ['a', 'b']
    }
};
var str = JSON.stringify(obj, undefined, 4);
 
output(syntaxHighlight(str));

最后生成的法力正是如此的:

图片 3

是还是不是轻松而又实用吧~

1 赞 1 收藏 评论

图片 4

本文由澳门新葡亰平台官网发布于web前端,转载请注明出处:深入理解Javascript面向对象编程,eval解析JSON字符

TAG标签:
Ctrl+D 将本页面保存为书签,全面了解最新资讯,方便快捷。