当前位置:千赢国际官网 > 千赢网页手机版登入 > 前端基础进阶,轻松学习

前端基础进阶,轻松学习

文章作者:千赢网页手机版登入 上传时间:2019-11-23

前端基础进阶(9):详解面向对象、构造函数、原型与原型链

2017/04/02 · JavaScript · 1 评论 · 原型, 原型链, 构造函数, 面向对象

原文出处: 波同学   

图片 1

.

如果要我总结一下学习前端以来我遇到了哪些瓶颈,那么面向对象一定是第一个毫不犹豫想到的。尽管我现在对于面向对象有了一些的了解,但是当初的那种似懂非懂的痛苦,依然历历在目。

为了帮助大家能够更加直观的学习和了解面向对象,我会用尽量简单易懂的描述来展示面向对象的相关知识。并且也准备了一些实用的例子帮助大家更加快速的掌握面向对象的真谛。

  • jQuery的面向对象实现
  • 封装拖拽
  • 简易版运动框架封装

这可能会花一点时间,但是却值得期待。所以如果有兴趣的朋友可以来简书和公众号关注我。

而这篇文章主要来聊一聊关于面向对象的一些重要的基本功。

轻松学习 JavaScript(7):对象属性描述符

2018/01/11 · JavaScript · 对象

原文出处: Dhananjay Kumar   译文出处:码农网/小峰   

在JavaScript中,你可以如下所示创建一个对象字面量:

var cat = { name: 'foo', age: 9 };

1
2
3
4
var cat = {
    name: 'foo',
    age: 9
};

乍一看,好像对象cat有字符串和数字值这两个属性。然而,这不仅仅是JavaScript解释器。在ES5中,介绍了属性描述符的概念。在我们继续讨论属性描述符之前,让我们试着回答几个问题:

  • 如何创建只读属性?
  • 如何制定不可枚举的属性?
  • 如何使属性不可配置?
  • 如何确定一个属性是否是只读的?

如果你理解属性描述符,那么你就可以回答所有这些问题。

请看下面的代码:

var cat = { name: 'foo', age: 9 }; var a = Object.getOwnPropertyDescriptor(cat, 'name'); console.log(a);

1
2
3
4
5
6
var cat = {
    name: 'foo',
    age: 9
};
var a = Object.getOwnPropertyDescriptor(cat, 'name');
console.log(a);

输出将如下所示:

图片 2

正如你在这里看到的,这个属性有四个特征:

value保存属性的数据,而writable,enumerable和configurable则描述属性的其他特征。接下来我们将对这些特征一一阐述。

理解JavaScript的作用域链

2015/10/31 · JavaScript · 作用域链

原文出处: 田小计划   

上一篇文章中介绍了Execution Context中的三个重要部分:VO/AO,scope chain和this,并详细的介绍了VO/AO在JavaScript代码执行中的表现。

本文就看看Execution Context中的scope chain。

一、对象的定义

在ECMAScript-262中,对象被定义为“无序属性的集合,其属性可以包含基本值,对象或者函数”

也就是说,在JavaScript中,对象无非就是由一些列无序的key-value对组成。其中value可以是基本值,对象或者函数。

// 这里的person就是一个对象 var person = { name: 'Tom', age: 18, getName: function() {}, parent: {} }

1
2
3
4
5
6
7
// 这里的person就是一个对象
var person = {
    name: 'Tom',
    age: 18,
    getName: function() {},
    parent: {}
}

创建对象

我们可以通过new的方式创建一个对象。

var obj = new Object();

1
var obj = new Object();

也可以通过对象字面量的形式创建一个简单的对象。

var obj = {};

1
var obj = {};

当我们想要给我们创建的简单对象添加方法时,可以这样表示。

// 可以这样 var person = {}; person.name = "TOM"; person.getName = function() { return this.name; } // 也可以这样 var person = { name: "TOM", getName: function() { return this.name; } }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 可以这样
var person = {};
person.name = "TOM";
person.getName = function() {
    return this.name;
}
 
// 也可以这样
var person = {
    name: "TOM",
    getName: function() {
        return this.name;
    }
}

访问对象的属性和方法

假如我们有一个简单的对象如下:

var person = { name: 'TOM', age: '20', getName: function() { return this.name } }

1
2
3
4
5
6
7
var person = {
    name: 'TOM',
    age: '20',
    getName: function() {
        return this.name
    }
}

当我们想要访问他的name属性时,可以用如下两种方式访问。

person.name // 或者 person['name']

1
2
3
4
person.name
 
// 或者
person['name']

如果我们想要访问的属性名是一个变量时,常常会使用第二种方式。例如我们要同时访问person的name与age,可以这样写:

['name', 'age'].forEach(function(item) { console.log(person[item]); })

1
2
3
['name', 'age'].forEach(function(item) {
    console.log(person[item]);
})

这种方式一定要重视,记住它以后在我们处理复杂数据的时候会有很大的帮助。

writable

属性的值是否可以更改是由writable特征决定的。如果writable设置为false,那么属性的值不能更改,JavaScript将忽略属性值中的任何更改。请看下面的代码:

var cat = { name: 'foo', age: 9 }; Object.defineProperty(cat, 'name', { writable: false }); console.log(cat.name); // foo cat.name = "koo"; // JavaScript will ignore it as writable is set to false console.log(cat.name); // foo

1
2
3
4
5
6
7
8
var cat = {
    name: 'foo',
    age: 9
};
Object.defineProperty(cat, 'name', { writable: false });
console.log(cat.name); // foo
cat.name = "koo"; // JavaScript will ignore it as writable is set to false
console.log(cat.name); // foo

你可以使用Object.defineProperty更改writable、enumerable和configurable特征的值。我们稍后会在这篇文章中详细讨论Object.defineProperty,但正如你在上面的代码片段中看到的那样,我们已经将writable属性设置为false,从而改变了name属性的值。JavaScript将忽略重新分配,并且name属性的值将保持为foo。

如果以严格模式运行代码,那么为了重新分配writable设置为false的属性值,JavaScript将抛出异常。请看下面的代码:

'use strict'; var cat = { name: 'foo', age: 9 }; Object.defineProperty(cat, 'name', { writable: false }); console.log(cat.name); // foo cat.name = "koo"; // error

1
2
3
4
5
6
7
8
'use strict';
var cat = {
    name: 'foo',
    age: 9
};
Object.defineProperty(cat, 'name', { writable: false });
console.log(cat.name); // foo
cat.name = "koo"; // error

在这里,JavaScript以严格模式运行,因此,当你重新分配name属性的值时,JavaScript将抛出异常,如下所示:

图片 3

这里的错误消息说,你不能赋值到只读属性。也就是说,如果属性的writable特征设置为false,那么属性将充当只读属性。

作用域

开始介绍作用域链之前,先看看JavaScript中的作用域(scope)。在很多语言中(C ,C#,Java),作用域都是通过代码块(由{}包起来的代码)来决定的,但是,在JavaScript作用域是跟函数相关的,也可以说成是function-based。

例如,当for循环这个代码块结束后,依然可以访问变量”i”。

JavaScript

for(var i = 0; i < 3; i ){ console.log(i); } console.log(i); //3

1
2
3
4
5
for(var i = 0; i < 3; i ){
    console.log(i);
}
 
console.log(i); //3

对于作用域,又可以分为全局作用域(Global scope)和局部作用域(Local scpoe)。

全局作用域中的对象可以在代码的任何地方访问,一般来说,下面情况的对象会在全局作用域中:

  • 最外层函数和在最外层函数外面定义的变量
  • 没有通过关键字”var”声明的变量
  • 浏览器中,window对象的属性

局部作用域又被称为函数作用域(Function scope),所有的变量和函数只能在作用域内部使用。

JavaScript

var foo = 1; window.bar = 2; function baz(){ a = 3; var b = 4; } // Global scope: foo, bar, baz, a // Local scope: b

1
2
3
4
5
6
7
8
9
var foo = 1;
window.bar = 2;
 
function baz(){
    a = 3;
    var b = 4;
}
// Global scope: foo, bar, baz, a
// Local scope: b

二、工厂模式

使用上面的方式创建对象很简单,但是在很多时候并不能满足我们的需求。就以person对象为例。假如我们在实际开发中,不仅仅需要一个名字叫做TOM的person对象,同时还需要另外一个名为Jake的person对象,虽然他们有很多相似之处,但是我们不得不重复写两次。

var perTom = { name: 'TOM', age: 20, getName: function() { return this.name } }; var perJake = { name: 'Jake', age: 22, getName: function() { return this.name } }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var perTom = {
    name: 'TOM',
    age: 20,
    getName: function() {
        return this.name
    }
};
 
var perJake = {
    name: 'Jake',
    age: 22,
    getName: function() {
        return this.name
    }
}

很显然这并不是合理的方式,当相似对象太多时,大家都会崩溃掉。

我们可以使用工厂模式的方式解决这个问题。顾名思义,工厂模式就是我们提供一个模子,然后通过这个模子复制出我们需要的对象。我们需要多少个,就复制多少个。

var createPerson = function(name, age) { // 声明一个中间对象,该对象就是工厂模式的模子 var o = new Object(); // 依次添加我们需要的属性与方法 o.name = name; o.age = age; o.getName = function() { return this.name; } return o; } // 创建两个实例 var perTom = createPerson('TOM', 20); var PerJake = createPerson('Jake', 22);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var createPerson = function(name, age) {
 
    // 声明一个中间对象,该对象就是工厂模式的模子
    var o = new Object();
 
    // 依次添加我们需要的属性与方法
    o.name = name;
    o.age = age;
    o.getName = function() {
        return this.name;
    }
 
    return o;
}
 
// 创建两个实例
var perTom = createPerson('TOM', 20);
var PerJake = createPerson('Jake', 22);

相信上面的代码并不难理解,也不用把工厂模式看得太过高大上。很显然,工厂模式帮助我们解决了重复代码上的麻烦,让我们可以写很少的代码,就能够创建很多个person对象。但是这里还有两个麻烦,需要我们注意。

第一个麻烦就是这样处理,我们没有办法识别对象实例的类型。使用instanceof可以识别对象的类型,如下例子:

var obj = {}; var foo = function() {} console.log(obj instanceof Object); // true console.log(foo instanceof Function); // true

1
2
3
4
5
var obj = {};
var foo = function() {}
 
console.log(obj instanceof Object);  // true
console.log(foo instanceof Function); // true

因此在工厂模式的基础上,我们需要使用构造函数的方式来解决这个麻烦。

configurable

属性的其他特征是否可以配置取决于configurable的值。如果属性configurable设置为false,则不能更改writable和enumerable的值。请看下面的代码:

var cat = { name: 'foo', age: 9 }; Object.defineProperty(cat, 'name', { configurable: false }); Object.defineProperty(cat, 'name', { enumerable: false });

1
2
3
4
5
6
var cat = {
    name: 'foo',
    age: 9
};
Object.defineProperty(cat, 'name', { configurable: false });
Object.defineProperty(cat, 'name', { enumerable: false });

在这里,我们将name属性的configurable设置为false。之后,我们将enumerable设置为false。如前所述,一旦一个属性的configurable设置为false,那么你就不能改变另一个特征。

对于上面的代码,JavaScript会抛出一个TypeError异常,如下图所示。你会得到无法重新定义属性名称的错误:

图片 4

在使用configurable的时候,你需要记住,改变configurable的值只能做一次。如果将属性的configurable设置为false,那么你就不能重新分配它;你无法撤消对configurable的更改。请看下面的代码:

var cat = { name: 'foo', age: 9 }; Object.defineProperty(cat, 'name', { configurable: false }); Object.defineProperty(cat, 'name', { configurable: true });

1
2
3
4
5
6
var cat = {
    name: 'foo',
    age: 9
};
Object.defineProperty(cat, 'name', { configurable: false });
Object.defineProperty(cat, 'name', { configurable: true });

我们在重新分配name属性的configurable,但是,JavaScript会对上述操作抛出一个TypeError,如下图所示。正如你所看到的,一旦configurable被设置为false,就不能撤销那个更改。

图片 5

另一个重要的事情是,即使configurable设置为false,writable也可以从true更改为false——但反之则不然。请看下面的代码:

var cat = { name: 'foo', age: 9 }; Object.defineProperty(cat, 'name', { configurable: false }); Object.defineProperty(cat, 'name', { writable: false }); cat.name = 'koo'; console.log(cat.name); // foo

1
2
3
4
5
6
7
8
var cat = {
    name: 'foo',
    age: 9
};
Object.defineProperty(cat, 'name', { configurable: false });
Object.defineProperty(cat, 'name', { writable: false });
cat.name = 'koo';
console.log(cat.name); // foo

如果不是在严格模式下,上面的代码不会抛出任何异常。正如我们前面所讨论的,即使configurable为false,writable也可以从true变为false,反之则不然。另一个需要牢记的重要事项是,你无法删除configurable设置为false的属性。

var cat = { name: 'foo', age: 9 }; Object.defineProperty(cat, 'name', { configurable: false }); delete cat.name; // wont delete as configurable is false console.log(cat.name); // foo delete (cat.age); // will be deleted console.log(cat.age); // undefined

1
2
3
4
5
6
7
8
9
var cat = {
    name: 'foo',
    age: 9
};
Object.defineProperty(cat, 'name', { configurable: false });
delete cat.name; // wont delete as configurable is false
console.log(cat.name); // foo
delete (cat.age); // will be deleted
console.log(cat.age); // undefined

在上面的代码中,你会发现JavaScript不会删除name属性,因为name属性的configurable设置为false。

作用域链

通过前面一篇文章了解到,每一个Execution Context中都有一个VO,用来存放变量,函数和参数等信息。

在JavaScript代码运行中,所有用到的变量都需要去当前AO/VO中查找,当找不到的时候,就会继续查找上层Execution Context中的AO/VO。这样一级级向上查找的过程,就是所有Execution Context中的AO/VO组成了一个作用域链。

所以说,作用域链与一个执行上下文相关,是内部上下文所有变量对象(包括父变量对象)的列表,用于变量查询。

JavaScript

Scope = VO/AO All Parent VO/AOs

1
Scope = VO/AO All Parent VO/AOs

看一个例子:

JavaScript

var x = 10; function foo() { var y = 20; function bar() { var z = 30; console.log(x y z); }; bar() }; foo();

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var x = 10;
 
function foo() {
    var y = 20;
 
    function bar() {
        var z = 30;
 
        console.log(x y z);
    };
 
    bar()
};
 
foo();

上面代码的输出结果为”60″,函数bar可以直接访问”z”,然后通过作用域链访问上层的”x”和”y”。

图片 6

  • 绿色箭头指向VO/AO
  • 蓝色箭头指向scope chain(VO/AO All Parent VO/AOs)

再看一个比较典型的例子:

JavaScript

var data = []; for(var i = 0 ; i < 3; i ){ data[i]=function() { console.log(i); } } data[0]();// 3 data[1]();// 3 data[2]();// 3

1
2
3
4
5
6
7
8
9
10
var data = [];
for(var i = 0 ; i < 3; i ){
    data[i]=function() {
        console.log(i);
    }
}
 
data[0]();// 3
data[1]();// 3
data[2]();// 3

第一感觉(错觉)这段代码会输出”0,1,2″。但是根据前面的介绍,变量”i”是存放在”Global VO”中的变量,循环结束后”i”的值就被设置为3,所以代码最后的三次函数调用访问的是相同的”Global VO”中已经被更新的”i”。

本文由千赢国际官网发布于千赢网页手机版登入,转载请注明出处:前端基础进阶,轻松学习

关键词: 千赢国际官网 www.qy8.vip