JiM-W

keep Moving


  • 首页

  • 分类

  • 关于

  • 归档

  • 标签

面试题

发表于 2017-04-13 | 分类于 forwork

总结一些平时在电脑上写的很溜,但是在面试白班测试总是写不好的一些面试题吧!个人不喜欢白板测试,没有那种敲代码的感觉,并且一些细节也不好把控。

1 编写函数sum(2,3)和sum(2)(3)都能得到5

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function sum(){
var cache = arguments[0];
if(!arguments){
return ;
}else if(arguments.length === 1){
return function(num){
return cache+num
};
}else if(arguments.length === 2 ){
return arguments[0]+arguments[1];
}
}
console.log(sum(2,3));
console.log(sum(2)(3));

2 url地址的操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var url =' http://item.taobao.com/item.htm?a=1&b=2&c=&d=xxx&e';
function getJson (url){
var flag = url.indexOf('?')+1;
console.log(flag);
var res = url.slice(flag);
console.log(res);
var arr = res.split('&');
var Json = {};
for(var i = 0 ; i <arr.length ;i++){
var temp = arr[i].split('=');
console.log(temp);
console.log(temp[0]);
console.log(temp[1]);
Json[temp[0]] = temp[1];
}
return Json ;
}
getJson(url);
console.log(getJson(url));

3 运算符的操作,主要是 + ++ - && || 以及运算符的优先级的问题(感兴趣可以看看我这篇总结)

这里在回顾一下

+ 运算符

  • 如果一个运算数是字符串,那么这个运算环境就是字符串环境,另外一个运算数,不论是布尔类型,数字类型,undefined null NaN 以及数组,对象都会转化为字符串,然后进行字符串拼接
  • 如果一个运算数是数字,另外一个运算数是布尔类型,会将布尔类型转化为数字
  • 如果一个运算数是数组或者对象,会将数组或者对象转化为原始数据类型(调用toString方法),然后进行运算(一般也就是将数组或者对象转化为字符串,所以后续也是进行的字符串的拼接)

- 运算符

  • -运算符提供的是一个数字运行环境,会将字符串,布尔类型的值转化为数字(Number和parseInt方法),如果转化失败则返回NaN
  • 如果有运算数是对象或者数组,会先调用对象或者数组的toString方法,然后调用Number和parseInt方法转化为数字,进行数字的运算,如果转化失败则返回NaN
1
2
3
4
5
6
7
8
9
10
11
12
console.log(true + false);//1
console.log({}+true);//[object Object]true
console.log([]+true);//true
console.log(true + []);//true
console.log(1 + [] + 1 );//11
console.log(typeof(1 + []));//string
console.log([].valueOf());//[]
console.log([] - true);//-1
console.log({} - true);//NaN
console.log(1 - [] - 1 );//0
console.log('2' - false);//2
console.log("2" - undefined);//NaN

4 有关闭包的一个测试 如何实现点击每个 li 输出其 index 值,注册事件之后,触发事件,然后事件监听器函数就会执行

1
2
3
4
5
<ul id = 'test'>
<li>ee</li>
<li>ee</li>
<li>ee</li>
</ul>
1
2
3
4
5
6
7
8
var liObj = document.getElementById('test').children;
console.log(liObj);
for(var i = 0 ; i < liObj.length ; i++){
liObj[i].onclick = function(){
console.log(i);
}
}
//这个是不行的 注册事件之后,当点击的时候,i的值已经是 3 的,所以每次输出都是3

利用闭包 返回一个函数作为listener

1
2
3
4
5
6
7
for(var i = 0 ; i < liObj.length ; i++){
liObj[i].onclick = (function(n){
return function(){
console.log(n);
}
})(i+1)
}

还有一个简单的实现思路就是 给每一个li设置一个属性即可

1
2
3
4
5
6
7
for(var i = 0 ; i < liObj.length ; i++){
liObj[i].setAttribute('index',i+1);
liObj[i].onclick = function(){
console.log(this);
console.log(this.getAttribute('index'));
}
}

5 类数组转化为数组 apply,call 第一个参数改变函数的this指向,第二个参数表示传入函数的参数

1
2
3
4
5
6
7
8
var arrayLike = {
0 : "hai",
1 : "Jhon",
2 : "nam",
length : 3
}
var ret = Array.prototype.slice.call(arrayLike,0);
console.log(ret);

6 数组的去重

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var arr = [1,2,3,4,5,5,3,1,8,9];
Array.prototype.unique = function(){
var arr = [this[0]];
for(var i = 0 ; i < this.length ; i++){
//如果新数组中没有当前比较项,则将当前比较项加入新数组
var flag = true ;
for(var j = 0 ; j < arr.length ; j++){
if(this[i] === arr[j]){
flag = false;
break ;
}
}
if(flag == true){
arr.push(this[i]);
}
}
return arr ;
}
console.log(arr.unique());

7 字符串的去重

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
Array.prototype.unique = function(){
var arr = [this[0]];
for(var i = 0 ; i < this.length ; i++){
//如果新数组中没有当前比较项,则将当前比较项加入新数组
var flag = true ;
for(var j = 0 ; j < arr.length ; j++){
if(this[i] === arr[j]){
flag = false;
break ;
}
}
if(flag == true){
arr.push(this[i]);
}
}
return arr ;
}
var str = 'aaasdafafaasdddfggh';
function detRep(str){
var arr = str.split('');
var newArr = arr.unique();
var newStr = newArr.join('');
return newStr ;
}
console.log(detRep(str));

8 break和continue

1
2
3
4
5
6
7
8
9
//break会直接跳出循环
var iNum = 0;
for (var i=1; i<10; i++) {
if (i % 5 == 0) {
break;
}
iNum++;
}
alert(iNum);//4
1
2
3
4
5
6
7
8
9
//continue会退出当前这一次的循环,进行下一次循环,后面表达式都不会在执行
var iNum = 0;
for (var i=1; i<10; i++) {
if (i % 5 == 0) {
continue;
}
iNum++;
}
alert(iNum);//8

9 + - 运算符以及NaN undefined

1
2
3
4
5
6
7
8
9
var a;//undefined
var b = a * 0;//NaN
console.log(b * 2 + "2" - 0 + 4);
if (b == b) { //false
console.log(b * 2 + "2" - 0 + 4);
} else {
console.log(!b * 2 + "2" - 0 + 4);
}
//输出26

主要考察以下知识点

  • 带有undefined的基本运算结果返回NaN : undefined*2 undefined+2等
  • 带有NaN的比较运算符结果返回false; NaN == NaN 也是false
  • x 和+ 运算中数字和布尔类型的运算,会将布尔类型转化为数字在进行运算 !b*2 = 2
  • 带有字符串的 + 性环境中,会将另外一个运算数转化为字符串,然后进行字符串的拼接 !b*2+’2’ : ‘22’;
  • - 性环境中,会将字符串,如果一个是字符串,另外一个是数字会将字符串转化为数字(即使两个都是字符串也会将两个都转化为字符串) ‘22’-0 : 22 数字类型的22
  • 如果没有 - 0 这步运算,那么返回的结果将是 字符串 ‘224’;

10 日期操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//假设今天是 2017-4-12
var date = new Date();
console.log(date.getFullYear());//2017
console.log(date.getMonth());//3 注意Month获取的月份是从0开始的
console.log(date.getDate());//12
var year = date.getFullYear();
var Month = date.getMonth()+1 ;
var day = date.getDate();
Month = Month < 10 ? '0'+Month : Month ;
day = day < 10 ? '0'+day : day ;
var res = year + '-'+ Month + '-'+day;
console.log(res);

11 实时获取页面中的元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<ul>
<li>Item</li>
<li>1</li>
<li>1</li>
<li>Item</li>
<li>Item</li>
</ul>
<script>
var items = document.getElementsByTagName('li');
for(var i = 0; i < items.length; i++){
if(items[i].innerHTML == '1'){
console.log(i);
items[i].parentNode.removeChild(items[i]);
console.dir(items);//items已经剩下四个了而此时的索引值并没有改变
}
}
</script>

执行后

1
2
3
4
Item
1
Item
Item

这里需要注意的一点是 DOM获取的元素 items是实时更新的;

12 变量声明提升 函数声明优先于变量声明

1
2
3
4
console.log(a);//是一个函数
var a = 3;
function a(){}
console.log(a);////3

live for life ? or live to life

发表于 2017-03-25 | 分类于 随笔

life or live ?

1 有时候,为了生活或者更好的生活,为了父母可以有个安稳的、健康的晚年,为了未来的家庭可以安稳,自己总要去经历很多,孤军奋战,默默地去努力,去奋斗.

​ 时光就是像沙漏,它溜走的速度超过你的想象,在北京漂泊总是回想过去的日子,有人陪你玩,有人陪你笑

​ 很多人说,孤独吗?那就对了,孤独就是你成长的时候;

​ 也有很多人说,能承受多大的委屈和孤独,就能撑起多大的梦想和责任;

其实我觉得,幸福感才是最重要的,一个人的幸福感来自于他对于生活节奏的把控,对于时间管理的合理化;如果感到了孤独,那就联络下朋友,新朋友,老朋友都可以;如果感到委屈了,那就去努力去忘掉那些委屈,没有谁的生活是不委屈的,没有背景,没有依赖,那就得靠自己;

2 很多人知道马云那句:梦想还是要有的,万一实现了呢?

我觉得 梦想可以有,

无论能不能实现,不放弃,不浮躁,埋下它的种子,

实现了则为它喝彩,

不实现,至少我们曾经为了它真正的战斗过;

坚持一下下,阳光总会来的;

3 毕业转眼两年了,对于市面上的各种鸡汤从来不感冒,一直觉得一个人的强大不在于外在的力量驱使,也不在于别人的鼓励和支持,一个人真正的强大来自于过去所有的时间的打磨和沉淀,或者说,一个人的不强大来自于过去所有的时间的打磨和沉淀;

愿我们每个正值青春奋斗的年纪的各位,走在路上,并且健康、积极的走在路上,成为自己想要成为的人,过自己想要的生活。

4 一个人要有自己的爱好,对于我来说,音乐和篮球是我爱好的主战场,有了它们,确实会让我感到平静许多;每次触摸到篮球,我就会感到充实和快乐;浮躁的时候听一些自己喜欢的音乐,会让自己安静下来。

有爱好的人是幸福的,它会让我们感到存在。

5 最近一段时间确实感到很累,记一笔吧。

​ 2017.3.20 12:48

继续学习,一点点啃掉各个技术难点。

NodeJs中的path路径浅析

发表于 2017-01-17 | 分类于 nodejs

1 node 服务器端 文件路径的相关问题

首先来看下基础概念

node提供了两个全局变量 _ filename (表示当前文件的绝对根路径) 和 _dirname (表示当前文件所在目录的绝对根路径)

先来看下测试代码的目录结构

1
2
3
4
5
6
7
F :
-workspace
-app
-hero-admin
-common.js
-path
-a.js

common.js

1
console.log('this is some data from hero-admin')

a.js

1
2
3
4
5
6
var path = require('path');
console.log('path__dirname=='+ __dirname);
console.log('path.process==' + __filename);
console.log('process.cwd=='+process.cwd());
console.log('path.resolve=='+path.resolve('./'));

第一种情况,在path目录下 执行命令 node a.js 在node 终端输出结果如下

1
2
3
4
//path__dirname==F:\workspace\app\path
//path.process==F:\workspace\app\path\a.js
//process.cwd==F:\workspace\app\path
//path.resolve==F:\workspace\app\path

第二种情况,在 app 目录下 执行命令 node path/a.js

1
2
3
4
//path__dirname==F:\workspace\app\path
//path.process==F:\workspace\app\path\a.js
//process.cwd==F:\workspace\app
//path.resolve==F:\workspace\app

通过上面的代码演示,我们可以发现, dirname 和 filename 的值是 和node命令执行的时候所在的目录没有关系的,无论我们在哪个目录执行 a.js 文件 dirnam 和 filename的值 永远都是返回 a.js 该文件所属目录的绝对路径(相对于根目录),这个特性记住,下面会有应用。

2 对于一般文件的引用的路径,会受到启动nodejs终端不同路径的影响

接下来我们改变下a.js文件的内容

1
2
3
4
5
6
7
var fs = require('fs');
fs.readFile('../hero-admin/common.js','utf-8',function(err,data){
if(err) return console.log(err);
console.log(data);
})

2.1 此时我们在path目录下运行 node a.js

可以得到common.js里面的内容

2.2 如果我们改在 app 目录下运行 node path/a.js

此时会抛出错误,找不到文件路径

1
2
3
4
5
6
7
8
{ Error: ENOENT: no such file or directory, open 'F:\workspace\hero-admin\common.js'
at Error (native)
errno: -4058,
code: 'ENOENT',
syscall: 'open',
path: 'F:\\workspace\\hero-admin\\common.js'
//可以看到此时请求的路径 ../hero-admin/common.js 是相对于 app 目录而言的
}

如果我们改下readFile的请求路径

还在app 目录下运行 node path/a.js

1
2
3
4
5
6
var fs = require('fs');
//相对于app当前目录,此时就可以得打文件的内容
fs.readFile('./hero-admin/common.js','utf-8',function(err,data){
if(err) return console.log(err);
console.log(data);
})

根据以上,我们可以得出结论

./ 和 ../ 是相对于启动服务器所在路径为基准的,而不是被启动的文件所在的路径为基准

这个是我们需要注意的一点,node中的文件的路径都是相对于启动node终端的那个目录为基准的,重要事情说两遍

同样包括如果我们引入的模块中也有相对路径的引用,此时,引用的模块中的路径还是相对于启动node终端的那个目录为基准的,重要事情说三遍

当然以上仅仅指的是文件的路径引用,会受到node启用终端的位置不同而受到影响,但是定义的模块不会受node终端启用的影响,这个稍后会有demo解释

2.3 / 代表服务器所在磁盘的根目录

修改readFile请求的路径

还在app 目录下运行 node path/a.js

1
2
3
4
5
var fs = require('fs');
fs.readFile('/hero-admin/common.js','utf-8',function(err,data){
if(err) return console.log(err);
console.log(data);
})
1
2
3
4
5
6
7
8
{ Error: ENOENT: no such file or directory, open 'F:\hero-admin\common.js'
at Error (native)
errno: -4058,
code: 'ENOENT',
syscall: 'open',
path: 'F:\\hero-admin\\common.js'
//可以看到服务器去F盘根目录去寻找这个文件了
}

2.4 但是我们在实际开发中总是避免不了对文件的路径的改变和移动,使得我们的项目结构更加清晰,或者说启动服务器位置不同,这个时候对于文件的相对路径问题如何解决?

还是以上目录结构 a.js 文件如下

1
2
3
4
5
6
7
8
9
var path = require('path');
console.log(__dirname);
console.log(path.join(__dirname,'../02hero-admin/common.js'));
var fs = require('fs');
fs.readFile(path.join(__dirname,'../02hero-admin/common.js'),'utf-8',function(err,data){
if(err) return console.log(err);
console.log(data);
})

我们在path目录下执行 node a.js 和我们在app目录下执行 node path/a.js

输出的结果都是一样的

1
2
3
F:\workspace\app\path
F:\workspace\app\hero-admin\common.js
console.log('this is some data from lib')

由此可见,我们可以利用 __dirname不受启动服务其所在路径的影响的这个特性,使用绝对路径来动态读取文件内容,

这个时候无论我们在任何位置启用a.js文件,路径问题就不在有了

3 对于模块的的引用的路径,不会受到启动nodejs终端的不同路径影响

我们队目录结构稍微做一下修改

1
2
3
4
5
6
7
8
F :
-workspace
-app
-hero-admin
-common.js
-myModule.js
-path
-a.js

myModule.js 我们就简单的返回一个对象

1
module.exports = {name:"Jhon",age:14};

a.js

1
2
var ret = require('../02hero-admin/myModule');
console.log(ret);

这个时候,无论我们是在任何目录启动node终端执行 a.js 都可以引用到myModule.js这个模块

也就是说模块的引用永远都是相对于a.js文件的,而文件的引用却是相对于启用node终端的路径

path目录下执行 node a.js 以及 app目录下执行 node path/a.js 终端输出如下

1
{ name: 'Jhon', age: 14 }

4 平常我们在部署项目的时候,路径的引用问题是一个令人头疼的问题,不过最基本的原理搞明白了,问题还是可以迎刃而解的。

the difference between null and undefined

发表于 2017-01-15 | 分类于 javascript

##apply call bind 对比分析

1 基本语法,第一个参数必须传入一个对象

1
2
3
4
5
fun.bind(thisArg[, arg1[, arg2[, ...]]])
fun.call(thisArg[, arg1[, arg2[, ...]]])
fun.apply(thisArg[, argsArray])
//bind call apply可以改变原来的 fn 函数以 thisarg为对象进行执行
//第一个参数是代表fun函数执行的this指向

2 三者之间的区别

2.1 bind只绑定方法执行的对象,并不执行方法,改变的是函数的this指向;根据bind的这个特性,经常会结合setTimeout执行

2.2 call apply绑定方法的执行对象的同时,也会直接执行方法;

2.2.1 fun.apply(thisArg[, argsArray])

thisArg
在 fun 函数运行时指定的 this 值。需要注意的是,指定的 this 值并不一定是该函数执行时真正的 this 值,如果这个函数处于非严格模式下,则指定为 null 或 undefined 时会自动指向全局对象(浏览器中就是window对象),同时值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的自动包装对象。
argsArray
一个数组或者类数组 对象,其中的数组元素将作为单独的参数传给 fun 函数 。如果该参数的值为null 或 undefined,则表示不需要传入任何参数。从ECMAScript 5 开始可以使用类数组对象。浏览器兼容性请参阅本文底部内容。

同时除了可以传入一个数组,也可以传入arguments,代表当前函数的参数的一个类数组

如果 argsArray 不是一个有效的数组或者不是 arguments 对象,那么将导致一个 TypeError。
如果没有提供 argsArray 和 thisArg 任何一个参数,那么 Global 对象将被用作 thisArg, 并且无法被传递任何参数。

2.2.2 fun.call(thisArg[, arg1[, arg2[, …]]])

如果thisArg指定为null和undefined的this值会自动指向全局对象(浏览器中就是window对象),同时值为原始值(数字,字符串,布尔值)的this会指向该原始值的自动包装对象。

2.3 主要改变函数体执行的时候,函数体内this的指向;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<script>
var name = "Jhon"
function getName (){
console.log(this.name)
}
var obj = {
name:"JiM"
}
getName();//Jhon
getName.apply(obj);//JiM
getName.call(obj);//JiM
getName.bind(obj);//不会执行getName函数
getName.bind(obj)(); //JiM
</script>

2.3 call方法传入的参数是参数列表形式的,apply传入的参数必须是数组 形式的

  • apply参数数组化的应用场景:求数组最值
1
2
3
4
5
6
var arr= [1,2,3,4,5,777]
// var max = Math.max.apply(null,arr);//777
var max = Math.max.call(null,arr);//NaN
// var max = Math.max(arr) ; //NaN
console.log(max);
// 这是apply方法的特性,apply方法第二个参数为参数的数组,虽然我们传入的是数组参数apply会将一个数组转化为一个参数接一个参数的传递给方法。但是call并不会
  • 如何将一个数组追加到另外一个数组呢?
1
2
3
4
var arr1 = [1,2,3];
var arr2 = [4,5,6];
var arr3 = arr1.concat(arr2);
//是的,这种方法可以,但是问题是concat方法并不会改变原来的数组,而是会返回一个新的元素

或者你也会这么做

1
2
3
4
5
6
7
var arr1 = [1,2,3];
var arr2 = [4,5,6];
//arr1.push(arr2)//这么做是不行的,push会将arr2整体添加到arr1中
for(var i = 0 ; i < arr2.length;i++){
arr1.push(arr2[i]);
}
//这么做也可以,通过循环,但是挺麻烦

看看apply参数数组化的优势 apply会将数组拆分,将每个元素传入调用的函数push

1
2
3
4
5
var arr1 = [1,2,3];
var arr2 = [4,5,6];
arr1.push.apply(arr1,arr2) //apply起了决定性的作用,将传入的数组或者类数组中的参数一个个的分开
// arr1.push(arr2) //这么做不行,直接将arr2整体添加给了arr1
console.dir(arr1);
  • 如何将一个 类数组对象 添加到另外一个 对象 呢?
1
2
3
4
5
6
7
8
9
10
var arrLike = {
0:"name",
1:"age",
2:"address",
length:3
//如果将类数组作为apply的参数传入(因为apply第二个参数必须是数组),类数组必须有length属性,如果没有该属性,那么类数组中的值不会被push进去;
}
var obj = { };
Array.prototype.push.apply(obj,arrLike); //关键还是apply方法会将传入的类数组元素一个个传递给push,然后push方法就可以将所有的元素以单独的形式添加给obj
console.dir(obj);

下面代码可以看下,看下apply的参数对于数组以及类数组的要求

1
2
3
4
5
6
7
8
9
10
11
12
13
var obj1 = {
name:"Jhon",
age:13,
address:"American",
1:"JiM",
other:{
gender:"man"
},
// length:4
}
var obj2 = {};
Array.prototype.push.apply(obj2,obj1)
console.dir(obj2);
  • apply操作DOM元素 NodeList类数组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div></div>
<div></div>
<div></div>
<script>
var divs = document.querySelectorAll("div");
console.dir(divs);//这是一个类数组
console.dir(divs[0]);
var obj = {};
Array.prototype.push.apply(obj,divs);//push+obj+apply可以将DOM NodeList转化为对象
console.dir(obj);
console.dir(obj[0]);
</script>
</body>
</html>

bind

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function bind(fn, obj) {
return function() {
return fn.apply(obj, arguments);
}
}
var obj = {
a: 20,
getA: function() {
setTimeout(bind(function() {
console.log(this.a)
}, this), 1000)
}
}
obj.getA();
//---------------------------------
var obj = {
a: 20,
getA: function() {
setTimeout(function() {
console.log(this.a)
}.bind(this), 1000)
}
}

3 this的指向

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//1 非严格模式下,this默认指向全局对象,call/apply显式指定this参数时也会强制转换参数为对象(如果不是对象)。其中,null/undefined被替换为全局对象,基础类型被转换为包装对象。
function nsm() {console.log(this);}
nsm(); // Window{top: xxxx}
nsm.call(null/undefined); // Window{top: xxxx}
nsm.call(1); // Number {[[PrimitiveValue]]: 1}
nsm.call('str'); // String {0: "s", 1: "t", 2: "r", length: 3, [[PrimitiveValue]]: "str"}
nsm.call(true); // Boolean {[[PrimitiveValue]]: true}
//----------------------------------------------------------------------------------------
//2 严格模式下,this默认为undefined,且call/apply显式指定this参数时也不会有强制转换
function sm() {'use strict'; console.log(this);}
sm(); // undefined
sm.call(null); // null
sm.call(undefined); // undefined
sm.call(1); // 1
sm.call('str'); // str
sm.call(true); // true

NodeJs Event

发表于 2017-01-15 | 分类于 nodejs

1 events事件

1
2
3
//引入events模块并创建eventEmitter对象
var events = require('events');
var eventEmitter = new events.EventEmitter();
1
2
//声明事件函数
var eventNameHandle = function eventName(){}
1
2
3
//绑定事件处理程序,并触发执行
eventEmitter.on('eventName',eventNameHandle);
eventEmitter.emit('eventName');

2 我们可以输出 events 模块 console.log(events); 查看其所包含内容

events模块只提供了一个对象: events.EventEmitter .该对象的核心就是事件的触发和事件的监听的整体封装;

events.EventEmitter 对象提供了多个属性,比如on可以绑定事件 emit用于触发事件,当事件触发的时候,注册到这个事件的监听器会被依次调用,事件参数作为回调函数参数传递;

on(event,listener) emit(event,arg1,arg2,····)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var events = require('events');
var eventsEmitter = new events.EventEmitter();
eventsEmitter.on("eventName",function(arg1,arg2){
console.log("listener1",arg1,arg2);
});
eventsEmitter.on("eventName",function(arg1,arg2){
console.log("listener2",arg1,arg2);
});
//以下给同一个事件注册了两个监听器
eventsEmitter.on("anotherName",function(arg1,arg2){
console.log("listener3",arg1,arg2);
});
eventsEmitter.emit('eventName','argone','argtwo');
//eventsEmitter.emit('anothertName','argone','argtwo');

once(event,listener) 为指定事件注册一个单次监听器,即监听器最多只能触发一次,触发之后马上解绑

1
2
3
4
5
6
7
8
var events = require('events');
var eventsEmitter = new events.EventEmitter();
eventsEmitter.once("name1",function(arg1,arg2){
console.log("listener4",arg1,arg2);
});
eventsEmitter.emit('name1','argone','argtwo');
eventsEmitter.emit('name1','argone','argtwo');
//在node终端执行的时候,即使emit了两次,该事件监听器也只执行一次,而on绑定的事件可以多次执行

addListener(event,listener) 给指定事件添加一个事件监听器,到监听器数组的尾部

removeListener(event,listener) 删除指定事件的listener,此操作会影响处于被删监听器之后的那些监听器的索引

listenerCount(emmit,listener) emmit是 new events.EventEmitter()对象,所有的事件都是通过该对象进行绑定,触发等操作。该方法可以返回指定事件的监听器数量

listeners(event) 该方法返回某个事件上的监听器所组成的数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var events = require('events');
var emmit = new events.EventEmitter();
function listener1 (){
console.log("this is listernr1");
}
function listener2(){
console.log("this is listener2");
}
emmit.on('event1',listener1);
emmit.on('event1',listener2);//给event1注册两个监听器,listenerConut可以得到该事件的监听器数量
//let count = emmit.listenerCount(emmit,'event1');
let count = require('events').EventEmitter.listenerCount(emmit,'event1');
console.log(count);//2
console.log(emmit.listeners('event1'));
//[ [Function: listener1], [Function: listener2] ]
emmit.emit('event1');
emmit.removeListener('event1',listener1);
emmit.on('event1',listener1);//这个监听事件被移除,不会再次被触发

NodeJsFileSystem WriteFileAndReadFile

发表于 2017-01-08 | 分类于 nodejs

FileSystem

文件 I/O 是由简单封装的标准 POSIX 函数提供的。 通过 require(‘fs’)使用该模块。 所有的方法都有异步和同步的形式。

  • 异步形式始终以完成回调作为它最后一个参数。 传给完成回调的参数取决于具体方法,但第一个参数总是留给异常。 如果操作成功完成,则第一个参数会是 null或 undefined,如果操作失败,则第一个参数将是一个错误对象;

    需要注意一点,异步操作无法通过try-catch来捕获异常

  • 当使用同步形式时,任何异常都会被立即抛出。 可以使用 try/catch 来处理异常,或让它们往上冒泡。

nodejs fs

1 读取文件操作readFile(file,options,callback)

  • file是要读取的文件的路径
  • options
    • encoding | null //默认值是null,所以如果不指定编码方式,读取文件的结果默认返回的值是二进制数据,返回的结果会作为参数传递给callback回调函数
    • flag 默认是 ‘r’
  • callback 回调函数又有两个参数,第一个参数是error对象,第二个参数是读取到的数据
    • 如果读取文件成功,err返回null data返回读取到的文件数据;(如果字符编码未指定,则返回原始的buffer数据)
    • 如果读取文件失败,err返回一个错误对象,data返回undefined;
  • 注意读取文件的内容返回的结果要么是Buffer类型的数据,要么是字符串类型的数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//创建文件读取对象
var fs = require('fs');
fs.readFile('input.txt',function(err,data){ //假如input.txt里面的内容是 hello world
console.log(arguments);//我们可以打印出来回调函数的参数进行查看,即使文件读取失败,回调函数也会执行
console.log(err);
console.log(data);//readFile返回的data数据是buffer类型的数据
if(err){
console.log(err.stack);
}
console.log(data.toString());//需要调用toString方法转化为字符串
});
console.log("program is done");
//program is done
//hello world
//程序无阻塞运行
1
2
3
4
5
6
//readFile语法
fs.readFile('etc/passwd',(err,data)=>{
if(err) throw err; //使用箭头函数可以使代码更加简洁
console.log(data);
});
//readFile用于异步读取文件内容,

2 写入文件的操作writeFile(file,data,[option],callback)

  • file可以是文件名或者文件描述
  • data是异步地写入的数据内容,可以是一个string或者buffer
  • option 编码格式,如果data是一个buffer,则默认encoding是utf-8
  • callback 回调函数,该回调函数里面只有一个参数就是err错误处理对象,如果写入成功err返回null

看下面这个简单的例子

1
2
3
4
5
6
7
8
9
10
var fs = require('fs');
fs.writeFile('../write.txtf','hello nodejs',function(err){
console.log(arguments);
if (err){
return console.log("写入文件失败")
}
console.log("写入文件成功");
});
console.log("pro is done");

需要注意的是,我们只能写入字符串或者Buffer类型的数据,如果写入复杂数据类型

1
2
3
4
5
fs.writeFileSync('./text',' sync write'); // 写入文件的内容是 sync write
fs.writeFileSync('./text.txt',{name:"Jhon"});//写入文件的内容是[object Object]
fs.writeFileSync('./text.txt',function(){});//写入文件的内容是 function(){}
fs.writeFileSync('./text.txt',[1,2,3]); //写入文件的内容是 1,2,3
fs.writeFileSync('./text.txt',1);//写入文件的内容是 1

3 以上文件的读写都是异步进行的,不会阻塞程序的执行,同时我们在日常工作中通过服务器端的数据的读写也会经常用到这两个API

4 对于异步 的任务,很多时候,每个任务的执行先后顺序是无法控制的,那么如何去解决这个问题?

此时,如果需要异步任务按照我们的意愿顺序进行,那么则需要将形成异步嵌套,形成一个回调链

NodeJs 事件驱动

发表于 2016-12-28 | 分类于 nodejs

1 在了解nodejs的事件驱动模型的时候,先来看下传统线程网络模型

1.1 传统线程网络模型

发起请求,请求进入web服务器(IIS、Apache)之后,会在线程池中分配一个线程来线性同步完成请求处理,直到请求处理完成并发出响应,结束之后线程池回收。

这就会就会带来以下几个问题 :

  • 由于线程池中线程个数有限,对于频繁请求时,就会出现等待,严重的甚至会把服务器挂掉
  • 同时线程的增多也会占用大量的 CPU 时间来处理内存上下文切换, 而且还容易遭受低速连接攻击
  • 对于高并发的时候,为了防止出现脏数据就会使用锁来解决,一些I/O事务可能消耗很长得时间,这样就会出现一些线程等待,效率低下

1.2 Node.Js使用事件驱动模型

当web server接收到请求,就把它关闭然后进行处理,然后去服务下一个web请求。当这个请求完成,它被放回处理队列,当到达队列开头,这个结果被返回给用户。这个模型非常高效可扩展性非常强,因为webserver一直接受请求而不等待任何读写操作。(这也被称之为非阻塞式IO或者事件驱动IO)

Node.js 的异步机制是基于事件的,所有的 磁盘 I/O 、 网络通信、 数据库查询 客户端的请求 都以非阻塞的方式请求,返回的结果由事件循环来处理。如图 描述了这个机制。Node.js 进程在同一时刻只会处理一个事件,完成后立即进入事件循环检查并处理后面的事件。
这样做的好处是CPU 和内存在同一时间集中处理一件事,同时尽可能让耗时的 I/O 操作并行执行

Nodejs事假驱动机制是通过Nodejs内部通过 单线程高效率地 维护事件队列来实现的,没有多线程的资源占用和频繁的上下文切换

  • 像Java、Python这个可以具有多线程的语言。多线程同步模式是这样的,将cpu分成几个线程,每个线程同步运行。
  • 而node.js采用单线程异步非阻塞模式,也就是说每一个计算独占cpu,遇到I/O请求不阻塞后面的计算,当I/O完成后,以事件的方式通知,继续执行计算

1.3 同步与异步

同步和异步关注的是消息通信机制 (synchronous communication/ asynchronous communication)

  • 所谓同步,就是在发出一个调用之后,在没有得到结果,该调用就不会返回;得到返回值之后,该调用返回
  • 所谓异步,就是在发出一个调用之后,直接返回该调用,所以没有返回结果;也就是说当一个异步调用发出后,调用者不会立刻得到结果,而是在调用发出后,被调用者通过状态的变化通知调用者,或者通过回调函数处理这个调用

举个通俗的例子:你打电话问书店老板有没有《分布式系统》这本书,如果是同步通信机制,书店老板会说,你稍等,”我查一下”,然后开始查啊查,等查好了(可能是5秒,也可能是一天)告诉你结果(返回结果)。而异步通信机制,书店老板直接告诉你我查一下啊,查好了打电话给你,然后直接挂电话了(不返回结果)。然后查好了,他会主动打电话给你。在这里老板通过“回电”这种方式来回调。

异步操作的时候回调函数的第一个参数通常是上一步传入的错误对象,异步操作不能使用try-catch捕获异常,因为在回调函数运行的时候,上一步的操作早就结束了,错误的栈也已经不存在了,所以只能将错误数据传递给回调函数进行处理

1.4 阻塞与非阻塞

常见I/O阻塞 磁盘读取 、 网络通信、 数据库查询 客户端的请求

阻塞和非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态.

  • 阻塞调用是指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。
  • 非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程。

还是上面的例子,你打电话问书店老板有没有《分布式系统》这本书,你如果是阻塞式调用,你会一直把自己“挂起”,直到得到这本书有没有的结果,如果是非阻塞式调用,你不管老板有没有告诉你,你自己先一边去玩了,
当然你也要偶尔过几分钟check一下老板有没有返回结果。在这里阻塞与非阻塞与是否同步异步无关。跟老板通过什么方式回答你结果无关。

1.5 线程和进程

  • 多线程单进程

多线程的 设计之初就是为了在共享的内存程序空间中,实现并行 处理任务,从而达到充分利用CPU的效果。多线程的缺点就是在于执行的时候上下文的切换开销比较大,使得程序的编写和调用复杂化

  • 单线程多进程

为了避免多线程造成的使用不便的问题,有的语言选择使用单线程保持调用的简单化,采用启动多进程的方式来达到充分利用CPU和提升整体的并行处理能力。它的缺点在于业务逻辑复杂的时候,涉及多个I/O操作的时候,因为业务逻辑不能分布到多个进程之间,事务处理时长要远远大于多线程模式。

2 代码实现,理解上述概念

2.1 阻塞与非阻塞

假如hello.txt

1
hello world

以下程序会非阻塞运行,当我们发起一个文件读取操作的时候,不会阻塞js代码的执行,后续的js代码会继续执行,当文件读取完毕之后,会调用回调函数;

1
2
3
4
5
6
7
8
9
10
11
var fs = require('fs');
fs.readFile('hello.txt',function(err,data){
if(err){
console.log(err.stack);
return;
}
console.log(data.toString());
});
console.log("pro is don");
//pro is don
//hello world

以下程序会阻塞运行,当我们发起一个文件读取操作的时候,会阻塞js代码的执行,当文件读取完毕之后,后续的js代码才开始执行

1
2
3
4
5
6
7
var fs = require("fs");
var data = fs.readFileSync('hello.js');
console.log(data.toString());
console.log("pro is done");
//hello world
//pro is don

2.2 事件循环线程与事件队列

对于遇到I/O操作,node.js不会停止后面的文件的内容的执行,会继续执行后续代码,然后另外一个线程处理I/O,处理完毕之后,将回调函数放入事件队列等待执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var fs = require('fs');
console.log("begin");
setTimeout(function(){
console.log("Timeout1");
},100)
fs.readFile('hello.tet',function(err,data){
if(err){
console.log(err.stack);
return;
}
console.log(data.toString());
});
setTimeout(function(){
console.log("Timeout2");
},0)
//即使设置为0,最少也会有5ms的延迟
console.log("end");

输出如下

1
2
3
4
5
begin
end
timeout2
timeout1
helloworld

需要注意的是 setTimeout和readFile的操作,会将回调函数放入事件队列,回调函数执行的顺序取决于文件读取的速度,以及延时任务的时间的大小比较;

通俗来讲,假如文件读取的用了200ms ,那么执行顺序就是上面的输出结果

假如文件读取用了50ms,那么执行顺序就是

1
2
3
4
5
begin
end
timeout2
helloworld
timeout1

3

NodeJs Util

发表于 2016-12-21 | 分类于 nodejs

1 Node.js Net 模块提供了一些用于底层的网络通信的小工具,包含了创建服务器/客户端的方法,我们可以通过以下方式引入该模块:

1.1 net相关API

net.js 文件

1
2
var net = require('net');
console.log(net);

执行命令 node net.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{ createServer: [Function],
createConnection: [Function],
connect: [Function],
_normalizeConnectArgs: [Function: normalizeConnectArgs],
Socket: { [Function: Socket] super_: { [Function: Duplex] super_: [Object] } },
Stream: { [Function: Socket] super_: { [Function: Duplex] super_: [Object] } },
Server:
{ [Function: Server]
super_:
{ [Function: EventEmitter]
EventEmitter: [Circular],
usingDomains: false,
defaultMaxListeners: [Getter/Setter],
init: [Function],
listenerCount: [Function] } },
_createServerHandle: [Function: createServerHandle],
isIP: [Function: isIP],
isIPv4: [Function: isIPv4],
isIPv6: [Function: isIPv6],
_setSimultaneousAccepts: [Function] }

1.2 net.createServer([options],[ connectionListener])
创建一个 TCP 服务器。参数 connectionListener 自动给 ‘connection’ 事件创建监听器。

connection事件会在一个新连接创建后被触发

net.socket对象实的实例现了一个双工流的接口

  • 用户创建客户端(使用 connect())时使用,
  • 由 Node 创建它们,并通过 connection 服务器事件传递给用户。

1.2.1 net.Socket类是 EventEmitter的实例有以下事件

  • data事件,当接收到数据的时候触发该事件,向该事件的监听器传入
  • end事件,当socket连接的另一端发出FIN包时被触发,也就是说数据传输完毕的时候触发该事件

2 Nodejs.path模块

1
2
3
4
5
6
7
8
9
10
11
12
13
var path = require("path");
// 格式化路径
console.log('normalization : ' + path.normalize('/test/test1//2slashes/1slash/tab/..'));
// 连接路径,用于根据电脑的不同的操作系统连接路径
console.log('joint path : ' + path.join('/test', 'test1', '2slashes/1slash', 'tab', '..'));
// 转换为绝对路径,会解析文件所在路径的绝对路径,如果是服务器就是相对于服务器根目录所在路径,如果是磁盘就是相对于磁盘的根目录
console.log('resolve : ' + path.resolve('main.js'));
// 路径中文件的后缀名
console.log('ext name : ' + path.extname('main.js'));
1
2
3
4
5
6
$ node main.js 执行main.js文件
normalization : /test/test1/2slashes/1slash
joint path : /test/test1/2slashes/1slash
resolve : /web/com/1427176256_27423/main.js
ext name : .js

ES6 class

发表于 2016-12-15 | 分类于 ES6

1 class declarations 可以用以下三种方式声明class类,必须先声明在使用;每一个使用class方式定义的类默认都有一个constructor函数, 这个函数是构造函数的主函数, 该函数体内部的this指向生成的实例

An important difference between function declarations and class declarations is that function declarations are hoisted(变量提升) and class declarations are not. You first need to declare your class and then access it,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var Rect = class Rect {
constructor(height,width){
this.height = height ;
this.width = width ;
}
}
var Rect = class {
constructor(height,width){
this.height = height ;
this.width = width ;
}
}
class Rect {
constructor(height,width){
this.height = height ;
this.width = width ;
}
}

2 对比ES5和ES6中的差别

constructor内定义的方法和属性是实例对象自己的,而constructor外定义的方法和属性则是所有实例对象可以共享的。

2.1 创建类的过程差别

ES5中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function RectAngle(height,width){
this.height = height ;
this.width = width ;
}
RectAngle.prototype.getArea = function(){
return this.calcArea();
}
RectAngle.prototype.calcArea = function(){
return this.height*this.width;
}
console.dir(RectAngle) ;//输出下构造函数
var rectAngle = new RectAngle(10,10);
console.log(rectAngle);
console.log(rectAngle.getArea());
console.log(rectAngle.__proto__ === Rectangle.prototype); //true

ES6中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Rect {
constructor(height,width){
this.height = height ;
this.width = width ;
}
//定义原型方法 类似于ES5中的Rect.prototype.getArea = function(){};
getArea(){
return this.calcArea();
}
calcArea(){
return this.height*this.width ;
}
}
console.dir(Rect) ;
const square = new Rect(10,10);
console.log(square);
console.log(square.getArea());
console.log(square.__proto__ === Rect.prototype); //true
//class类的prototype属性和其实例化对象的__proto__ 全等,这点和ES5中的构造函数prototype和其实例化对象的__proto__全等一致

2.2 类中的方法独立调用的时候,函数内部this指向不同

ES5中:原型上的方法被独立调用,非严格模式下,this会指向window,严格模式下指向undefined

1
2
3
4
5
6
7
8
9
10
11
function RectAngle(height,width){
this.height = height ;
this.width = width ;
}
RectAngle.prototype.getArea = function(){
return this;
}
var rectAngle = new RectAngle(10,10);
let getArea = rectAngle.getArea;
console.log(getArea());//window

ES6中: static方法或者原型上的方法被独立调用的时候,无论是否严格模式,其this指向都是undefined

1
2
3
4
5
6
7
8
9
10
11
12
13
class Rect {
constructor(height,width){
this.height = height ;
this.width = width ;
}
//定义原型方法 类似于ES5中的Rect.prototype.getArea = function(){};
getArea(){
return this;
}
}
const square = new Rect(10,10);
let getArea = square.getArea;
console.log(getArea());//undefined

2.3 class类中static声明的方法不能被实例调用,也不会出现在实例化对象上 ; 可以直接通过类名调用;

The static keyword defines a static method for a class. Static methods are called without instantiating )their class and cannot be called through a class instance. Static methods are often used to create utility functions for an application.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Rect {
constructor(height,width){
this.height = height ;
this.width = width ;
}
getArea(){ //这个是Rect类的属性prototype上的方法
return this.calcArea();
}
calcArea(){
return this.height*this.width ;
}
//静态方法为class类定义了一个方法,该方法不能再class类的实例对象上使用
static shortHBW (H,W){
return H-W ;
}
}
console.log(Rect);//static方法其实就是class类的属性
console.log(Rect.shortHBW(21,12) );//9 //static方法直接通过类名可以直接调用
console.log(Rect.getArea() ); //prototype method 不能直接通过类名调用

2.4 先来看下extends关键字的作用 class类实现继承的根本原因就是通过extends关键字,将子类的 proto 属性指向父类构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(this.name + ' makes a noise.');
}
}
class Dog { //没有继承的情况下
speak1() {
console.log(this.name + ' barks.');
}
}
console.dir(Dog);
console.dir(Animal);
console.log(Dog.__proto__ === Animal);//fasle
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(this.name + ' makes a noise.');
}
}
class Dog extends Animal{ //extends的作用 是将子类(函数) 的__proto__属性指向父类(函数)
,由此可以实现继承父类所有的属性和方法
speak1() {
console.log(this.name + ' barks.');
}
}
console.dir(Dog);
console.dir(Animal);
console.log(Dog.__proto__ === Animal);//true 这个是extends关键字的作用核心
var dog = new Dog();
console.log(dog);
console.log(dog.__proto__ === Dog.prototype);//true 实例化的对象的原型属性指向的是构造函数的原型,而构造函数的原型属性指向的是父类构造函数

2.5 constructor 方法,以及super关键字

2.5.1 The constructor method is a special method for creating and initializing an object created with a class; There can only be one special method with the name “constructor” in a class. A SyntaxError will be thrown if the class contains more than one occurrence of a constructor method.

A constructor can use the super keyword to call the constructor of a parent class.

constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor方法,如果没有显式定义,一个空的constructor方法会被默认添加。constructor函数默认返回实例对象

  • 当创建一个新类的时候,如果没有显式的添加constructor函数,那么默认将会添加一个空的constructor函数
1
2
3
4
5
6
7
8
9
class Parent {
}
//等价于
class Parent {
constructor(){
}
}
  • 当子类继承父类的时候,如果子类没有显式的添加constructor函数,那么默认将会添加一个constructor,并且自动调用super方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Parent(){
constructor(x){
this.x = x
}
}
class Child extends Parent{
}
//等价于
class Child extends Parent{
constructor(){
super()
//此处的super虽然代表了父类的构造函数,但是其返回的是子类Child的实例对象,即super内部的this指的是Child类,相当于super( ) ==> Parent.prototype.constructor.call(this)
}
}

2.5.2 super有三种作用, 第一是作为构造函数直接调用,第二种是作为父类实例, 第三种是在子类中的静态方法中调用父类的静态方法;super关键字,它指代父类的实例(即父类的this对象)。子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类没有自己的this对象,而是继承父类的this对象,然后对其进行加工。如果不调用super方法,子类就得不到this对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Cat {
constructor(name,color) {
this.name = name;
this.color = color;
}
speak() {
console.log(this.name + ' makes a noise.');
}
}
class Lion extends Cat {
constructor(name,color,age){
super(name,color);//第一个作用,作为构造函数直接调用,必须先调用,用来确定子类实例对象的this指向
this.age = age ;
}
speak() {
super.speak();//第二个作用,super 作为父类实例调用父类的方法
console.log(this.name + ' roars.');
}
}
var lion = new Lion("JErry","white",12);
lion.speak();

2.6 classs类中的 get 和 set 对某个属性设置存值函数和取值函数, 拦截该属性的存取行为

下面这个栗子是对name属性的设置值以及获取值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class People {
constructor(name) { //构造函数
this.name = name;
}
get name() {
return this._name.toUpperCase();
}
set name(value) {
this._name = value;
}
sayName() {
console.log(this.name);
}
}
var p = new People("tom");
console.log(p);
console.log(p.name); //TOM
console.log(p._name); //tom
p.sayName(); //TOM

ES6 object 对象新词法

发表于 2016-12-13 | 分类于 ES6

ES6 Object

1 对象新词法:允许在声明对象字面量时使用简写语法,来初始化属性变量和函数的定义方法,直接写变量,这时,属性名为变量名, 属性值为变量的值。

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
function getPerson(name,age,gender){
return {
name,
age,
gender,
sayHello(){
console.log('my name is'+this.name);
}
};
}
//等价于
function getPerson(name,age,gender){
return {
name: name ,
age :age ,
gender: gender,
sayHello:function(){
console.log('my name is'+this.name);
}
};
}
let p = getPerson("Jhon",18,'man');
console.log(p);
p.sayHello();
//当然了混合这新词法和以前的定义方式定义对象也是可以的
1
2
3
4
var foo = 'bar';
var obj = {foo};
//等价于
var obj = {foo:foo};//变量名:变量值

1.1 ES6 允许在声明对象的时候,对象的属性名可以是表达式,同时方法名也可以是表达式;

1
2
3
4
5
6
//javascript语言中定义对象的属性时候有以下两种方式
var obj = { } ;
//方式一
obj.foo = 'bar';
//方式二
obj['na'+'me'] = "Jhon";
1
2
3
4
5
6
//在ES5中声明对象字面量时,只允许用第一种方式声明属性名,在ES6中允许使用第二种表达式的方式声明属性名
//ES5中
var obj = {foo:true,abc:123,sayHello:function(){console.log('hello you')}};
//ES6中,对象的属性名可以是表达式
var property = 'foo';
var obj = {[property]:true,['a'+'ba']:123,['say'+'Hello'](){console.log('hello you')}}

1.2 注意一点,对象的属性名可以是表达式,但是不能和简写语法一起用

1
2
var property = 'foo';
var obj = {[property]} ;//会报错

1.3 属性名表达式如果是一个对象的话,默认情况下会转化为对象的字符串表示,属性名会覆盖

1
2
3
4
5
6
7
const keyA = {a: 1};
const keyB = {b: 2};
const myObject = {
[keyA]: 'valueA',
[keyB]: 'valueB'
};
myObject // Object {[object Object]: "valueB"}

2 对象超类 super,当类继承或者设置了一个对象的原型 proto 的时候,该对象的内使用super的时候,super可以理解为指向该对象的 proto 属性;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var parent = {
foo(){
console.log("this is parent");
}
}
var child = {
foo(){
super.foo();
console.log("this is child");
}
}
console.log(parent);
console.log(child);
Object.setPrototypeOf(child,parent);//改变child对象的 __proto__指向parent
child.foo();//this is parent this is child
child.__proto__.foo();//this is parent
1…567…20
JiM-W

JiM-W

keep fighting!

195 日志
52 分类
90 标签
© 2017 JiM-W
由 Hexo 强力驱动
主题 - NexT.Pisces