为了面试开始认真看一些前端的知识,发现我的基础其实并不好,都是一些很虚的理解,不能具体的表达出来,因此把自己看的东西都记一下,一方面练习一下Markdown的使用,一方面总结一下方便以后查看。
【CSS篇】
1、CSS盒子模型,绝对定位和相对定位
盒子模型也叫框模型,它规定了元素框处理元素内容、内边距、边框 和 外边距 的方式。
子绝父相,即相对定位是给父级的,绝对定位的时候是给子级的。
Absolute:绝对定位,是相对于最近的且不是static定位的父元素来定位。元素会脱离文档流,若该元素没有设置宽度,则宽度由元素里面的内容决定,且宽度不会影响父元素,定位为absolution后,原来的位置相当于是空的,下面的的元素会来占据。
第二个div设置了absolute,则div宽度由文本决定,且下面的div会上移占据之前第二个div的位置,top和left是相对于离它最近且不是static定位的父元素来定位的,在此div2因为没有父元素,所以第二个div相对于根元素即html元素来定位。
Relative:相对定位,是相对于其原本的位置来定位的。元素仍处于文档流中,定位是相对于原本自身的位置,若没有设置宽度,则宽度为父元素的宽度,该元素的大小会影响父元素的大小。
第二个div设置为相对定位
2、Node.js
作者:张戎 链接:www.zhihu.com/question/33… 来源:知乎 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
我们在用浏览器访问服务器的时候,就好像去茶叶铺买茶喝。我们可以买回来自己沏,也可以在店里自带的茶座儿坐下来喝现成的。如果我们买回来喝,参考华老师的课文,他自己都说了这是「小题大作」,因为16分钟和20分钟差不了太多,就我们自己和家人,慢生活4分钟也没什么毛病。
但是如果我们在店里喝,那可不是咱一家儿等着。比起16分钟的最少等候时间,第一桌多等4分钟,第二桌要同时来的话得多等24分钟,第三桌站起来走了,20多张桌子都闲着。忙活了半天,茶客不耐烦,茶铺不赚钱。所以,茶叶铺一定要学好小学课文,不仅对于同一桌的不同请求要统筹安排,别认死理一件事不干完绝不开始下一件,甚至对于不同桌的不同请求也要尽量并行处理,比如等开水时可以帮好几桌点单、拿茶叶、洗茶杯茶壶,水开之前别闲着,水一开就给各桌上茶,让每桌客人都感觉嗖快嗖快的。
在这个故事里,茶叶铺就是网络服务器。我们自己就是浏览器。我们要是不想浏览器事必躬亲,那就把活扔给服务器干;当服务器一下子服务很多浏览器时就不能认死理非要串行操作,要灵活统筹,同时开始几件事,哪件完事关闭哪件。
这三个特征用江湖切口说就叫:
服务器端JavaScript处理:server-side JavaScript execution
非阻断/异步I/O:non-blocking or asynchronous
I/O事件驱动:Event-driven
Node.js就是这样一个服务器端的、非阻断式I/O的、事件驱动的JavaScript运行环境。所以说,Node.js是华罗庚的路数,帮茶叶铺更快更多地伺候茶客。这麻利的店小二谁家养的?大家。因为Node.js是开源的。
3、清除浮动
清除浮动的方法:
1)、额外标签法(在最后一个浮动标签后,新加一个标签,给其设置clear:both;)(不推荐)
2)、父级添加overflow属性(父元素添加overflow:hidden)overflow:hidden属性相当于是让父级紧贴内容,这样即可紧贴其对象内内容(包括使用float的div盒子),从而实现了清除浮动
3)、对父级设置适合CSS高度
4、如何保持浮层水平垂直居中
1)、利用绝对定位与transform
父元素设置为相对定位,子元素为绝对定位
.children {
position: absolute;
top: 50%;
left: 50%;
-webkit-transform:translate(-50%,-50%);
background: black;
}
-webkit-transform:translate(-50%,-50%)表示向上、向左移动自身的百分之五十
2、利用margin负值
父元素相对定位,子元素绝对定位,使用margin负值为子元素长宽的一半来实现
<div class="parent">
<div class="children"></div>
  <span></span>
</div>
.parent{
height:400px;
position: relative;
background: red;
}
.children{
width: 200px;
height: 200px;
margin: -100px 0 0 -100px;
background: black;
position: absolute;
top: 50%;
left:50%;
}
如何实现水平居中 (www.jianshu.com/p/f85264cec… )
1)、行内元素
将父元素的text-align属性设置为center
2)、块级元素
定宽块级元素水平居中 只需要加margin:0 auto
不定宽块级元素水平居中 ①通过给要居中显示的元素,设置display:table,然后设置margin:0 auto来实现 ②(多个块状元素时)子元素设置inline-block,同时父元素设置text-align:center
如何实现
如何实现垂直居中(www.cnblogs.com/clj2017/p/9… )
1)、行内元素
将vertical-align设置为center
2)、display:flex
父元素display:flex;而子元素align-self:center
5、position 和 display 的取值和各自的意思和用法
position取值:static、relative、absolute、fixed、inherit。
static:让top,right,bottom,left的值失效。在切换的时候可以尝试这个方法。
inherit。规定从父类继承position属性的值。但是任何版本的IE都不支持该属性值。
display属性取值:none、inline、inline-block、block、flex、inherit。
display属性规定元素应该生成的框的类型。文档内任何元素都是框,块框或行内框。
display:none和visiability:hidden都可以隐藏div,区别有点像absolute和relative,前者不占据文档的空间,后者还是占据文档的位置。
inline和block,又叫行内元素和块级元素。表现出来的区别就是block独占一行,在浏览器中通常垂直布局,可以用margin来控制块级元素之间的间距;而inline以水平方式布局,垂直方向的margin和padding都是无效的,大小跟内容一样,且无法设置宽高。inline就像塑料袋,内容怎么样,就长得怎么样;block就像盒子,有固定的宽和高。
inline-block就介于两者之间。
display: flex 意为"弹性盒布局模型",用来为盒状模型提供最大的灵活性。任何一个容器都可以指定为flex布局。设为Flex布局以后,子元素的float、clear和vertical-align属性将失效。采用flex布局的元素,称为flex容器。它的所有子元素自动成为容器成员,称为flex项目(flex item)。是一种非常灵活的布局方式
flex的应用场景: www.9xkd.com/course/3156…
display:inherit 继承
【JS篇】
1、JS的数据类型有哪些?解释清楚 null 和 undefined,解释清楚原始数据类型和引用数据类型。比如讲一下 1 和 Number(1)的区别
1)、 基本数据类型:字符串 String、数字 Number、布尔Boolean
复合数据类型:数组 Array、对象 Object
特殊数据类型:Null 空对象、Undefined 未定义
2)、null 和 undefined 的区别
null表示一个空对象指针,使用typeof运算得到 “object” ,所以可以认为它是一个特殊的对象值。
Undefined类型,当一个声明了的变量未初始化时,得到的就是 undefined。
null 和 undefined 都表示 “值的空缺”,你可以认为 undefined 是表示系统级的、出乎意料的或类似错误的值的空缺,而null是表示程序级的、正常的或在意料之中的值的空缺。
3)、原始数据类型与引用数据类型
原始数据类型 存储在栈(stack)中的简单数据段,也就是说,它们的值直接存储在变量访问的位置。栈区包括了 变量的标识符和变量的值。
引用数据类型 存储在堆(heap)中的对象,也就是说,存储在变量处的值是一个指针(point),指向存储对象的内存处。放在栈空间中的值是该对象存储在堆中的地址。
2、prototype 是什么东西,原型链的理解,什么时候用 prototype(好难!)
1)、prototype(原型)是什么东西
原型的概念:每一个javascript对象(除null外)创建的时候,就会与之关联另一个对象,这个对象就是我们所说的原型,每一个对象都会从原型中“继承”属性。
JS中所有的东西都是对象,每个对象都有prototype这个属性,这个属性是一个对象(object)
所有的东西都由Object衍生而来,即所有东西原型链的终点指向Object.prototype
一张图表示构造函数和实例原型之间的关系:
2)、原型链
每个对象都可以有一个原型_proto_,这个原型还可以有它自己的原型,以此类推,形成一个原型链。查找特定属性的时候,我们先去这个对象里去找,如果没有的话就去它的原型对象里面去,如果还是没有的话再去向原型对象的原型对象里去寻找...... 这个操作被委托在整个原型链上,这个就是我们说的原型链了。
3)、什么时候使用原型链
简单的来说,就是需要clone一个对象时使用原型链
详细:Prototype通常用来解决一个问题:对象的创建比较耗费资源。比如,对象创建的时候需要访问数据库、需要读取外部文件、需要使用网络,这些都是比较耗费时间和内存的。
使用 prototype的好处是不会额外产生内存,所有实例化后的对象都会从原型上继承这个方法。也就是需要一个子类拥有父类的某些特性(同种特性可以覆盖),又可以添加自己的特性,而不会影响父类时候使用prototype。
3、函数里的this什么含义,什么情况下,怎么用
this是Javascript语言的一个关键字。它代表函数运行时,自动生成的一个内部对象,表示函数调用时的上下文,只能在函数内部使用。随着函数使用场合的不同,this的值会发生变化。但是有一个总的原则,那就是this指的是,调用函数的那个对象。
情况一:纯粹的函数调用
这是函数的最通常用法,属于全局性调用,因此this就代表全局对象Global。
var x = 1;
function test(){
alert(this.x);
}
test(); // 1
情况二:作为对象方法的调用
函数还可以作为某个对象的方法调用,这时this就指这个上级对象。
function test(){
alert(this.x);
}
var o = {};
o.x = 1;
o.m = test;
o.m(); // 1
情况三: 作为构造函数调用
所谓构造函数,就是通过这个函数生成一个新对象(object)。这时,this就指这个新对象。
function test(){
this.x = 1;
}
var o = new test();
alert(o.x); // 1
情况四: apply调用
apply()是函数对象的一个方法,它的作用是改变函数的调用对象,它的第一个参数就表示改变后的调用这个函数的对象。因此,this指的就是这第一个参数。
var x = 0;
function test(){
alert(this.x);
}
var o={};
o.x = 1;
o.m = test;
o.m.apply(); //0
apply()的参数为空时,默认调用全局对象。因此,这时的运行结果为0,证明this指的是全局对象。
4、apply和 call 什么含义,什么区别?什么时候用
这两个方法的作用是在函数调用时改变函数的执行上下文,也就是函数内的this;这两个方法第一个参数,就是希望得到的this。
它们的不同之处:
apply:最多只能有两个参数——新this对象和一个数组argArray。如果给该方法传递多个参数,则把参数都写进这个数组里面,当然,即使只有一个参数,也要写进数组里。如果argArray不是一个有效的数组或arguments对象,那么将导致一个TypeError。如果没有提供argArray和thisObj任何一个参数,那么Global对象将被用作thisObj,并且无法被传递任何参数。
call:它可以接受多个参数,第一个参数与apply一样,后面则是一串参数列表。这个方法主要用在js对象各方法相互调用的时候,使当前this实例指针保持一致,或者在特殊情况下需要改变this指针。如果没有提供thisObj参数,那么 Global 对象被用作thisObj。
实际上,apply和call的功能是一样的,只是传入的参数列表形式不同。
什么时候使用:判断变量类型时可以使用
我们在使用typeof判断变量类型的时候,如果是array,Date等,都返回object,所以就没法判断具体是哪一种了,这时候可以使用call
var array = [];
console.log(typeof array); // object
Object.prototype.toString.call(array); //[object,array]
function bar(){
console.log(Object.prototype.toString.call(this));
// 用来调用一些无法直接调用的方法
}
bar.call(7); //"[object Number]"
5、数组与对象有哪些原生方法?
string——charAt() 方法用于返回指定索引处的字符
连接两个数组的方法:concat()
ver a = [1,2,3];
var b = [4,5];
a.concat(b);//1,2,3,4,5
删除指定元素的方法:splice(index,len,[item])
注释:该方法会改变原始数组。
splice有3个参数,它也可以来替换/删除/添加数组内某一个或者几个值
index:数组开始下标 len: 替换/删除的长度 item:替换的值,删除操作的话 item为空
6、闭包
www.ruanyifeng.com/blog/2009/0…
简单理解——闭包就是定义在一个函数内部的能够读取该函数内部变量的函数,在本质上,闭包就是将函数内部和函数外部连接起来的一个桥梁。
使用闭包的注意点
1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
return function(){
return this.name;
};
}
};
alert(object.getNameFunc()());//The Window
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
var that = this;
return function(){
return that.name;
};
}
};
alert(object.getNameFunc()());//My Object
this始终表示调用者的应用,第一个的闭包返回出来的一个函数,就是在window的环境下调用了这个函数,所以这个this是指向的window,而第二个把this保存在了that中。
7、如何减少全局变量污染
1)、定义全局变量命名空间
只创建一个全局变量,并定义该变量为当前应用容器,把其他全局变量追加在该命名空间下
var myApp = {};
myApp.students = {
name:"小明",
age:18
};
myApp.teachers = {
A: {
姓名:"李老师",
gender:"男"
},
B: {
姓名:"朱老师",
gender:"女"
}
};
2)、使用闭包
使用闭包,也能显著减少全局变量污染,就是创建一个函数,该函数包括,私有变量和一个特权对象,特权对象的内容是,利用闭包能访问到私有变量的函数,最后返回特权对象。
//创建函数,返回一个特权对象
var f3 = function() {
var age = 18;
return {
name: "啊哈",
age: age,
gender: "男"
}
}();
//获取变量
f3.name = "啊哈";
8、ES5严格模式的作用,ES6箭头函数和ES5普通函数一样吗?
ECMA Script是一种由ECMA组织(前身为欧洲计算机制造商协会)制定和发布的脚本语言规范
ES5 即ECMAScript5 ,是javascript的语言的标准的一版。
es5是当今大部分浏览器用来编译网站的,es6正式版是2015年出来的,现在应该还在不断更新中
从es5开始,javscript有两种运行模式:正常模式和严格模式('use strict')。
严格模式的作用是:
1、消除javascript语法的一些不合理、不严谨之处,减少一些怪异行为;
2、消除代码运行的一些不安全性,促进代码运行的安全;
3、提高编译器效率,增加运行速度;
4、为未来新版本的javascript做好铺垫
ES6箭头函数与ES5普通函数有区别
es6箭头函数内部没有this,使用时会上朔寻找最近的this
不可以做构造函数,不能使用new命令,因为没有this
函数体内没有arguments,可以使用rest参数代替
不能用yield,不能使用generator函数
箭头函数,(x)=>x+6相当于function(x){return x+6}
9、ES6中let与var的区别
let定义的变量是块级的变量。var定义的变量是全局变量或者函数变量。let定义的变量只对它所在的区域内有效,而var定义的变量范围最少是一个函数之内。 www.cnblogs.com/fly_dragon/…
10、JS中true与false
首先理解==与===的区别
==会把比较的二者进行类型转换
如:0 == false; // true, 会把0转成bool值进行比较
===不会把比较的二者进行类型转换,是string就是string,是number就是number
0 === false; // false, 因为0是number,false是boolean,两者就肯定不相等
以下条件为false,也可称为“falsy” - > 虚值
if (false)
if (null)
if (undefined)
if (0)
if (0n)
if (NaN)
if ('')
if ("")
if (``)
if (document.all)
以下条件为true,也可称为“truthy”->真值
if (true)
if ({})
if ([])
if (42)
if ("0")
if ("false")
if (new Date())
if (-42)
if (12n)
if (3.14)
if (-3.14)
if (Infinity)
if (-Infinity)
11、js变量提升
变量声明提升:
如果变量声明在函数里面,则将变量声明提升到函数的开头
如果变量声明是一个全局变量,则将变量声明提升到全局作用域的开头
变量运行(搜索)机制:
首先看,有没有局部作用域
如果有,查找是不是这个局部作用域定义的变量
如果不是,寻找上一级作用域,直到找到全局作用域
如果全局作用域也找不到这个变量,这个变量就是未定义的 undefined
变量优先级低于形参的传递
segmentfault.com/a/119000001…
12、深拷贝与浅拷贝
赋值和深/浅拷贝的区别
-
当我们把一个对象赋值给一个新的变量时,赋的其实是该对象的在栈中的地址,而不是堆中的数据。也就是两个对象指向的是同一个存储空间,无论哪个对象发生改变,其实都是改变的存储空间的内容,因此,两个对象是联动的。
-
浅拷贝:重新在栈中创建内存,拷贝前后对象的基本数据类型互不影响,但拷贝前后对象的引用类型因共享同一块内存,会相互影响。
-
深拷贝:从栈内存中开辟一个新的区域存放新对象,对对象中的子对象进行递归拷贝,拷贝前后的两个对象互不影响。
浅拷贝的实现
- object.assign()
- 函数库lodash的_.clone方法
- 展开运算符...
- Array.prototype.concat()
- Array.prototype.slice()
接下来依次进行介绍
- object.assign()
const target = { a: 1 };
const source1 = { b: 2 };
const source2 = { c: 3 };
Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}
Object.assign方法的第一个参数是目标对象,后面的参数都是源对象。
let obj1 = { person: {name: "kobe", age: 41},sports:'basketball' };
let obj2 = Object.assign({}, obj1);
obj2.person.name = "wade";
obj2.sports = 'football'
console.log(obj1); // { person: { name: 'wade', age: 41 }, sports: 'basketball' }
- 函数库lodash的_.clone方法
var _ = require('lodash');
var obj1 = {
a: 1,
b: { f: { g: 1 } },
c: [1, 2, 3]
};
var obj2 = _.clone(obj1);
console.log(obj1.b.f === obj2.b.f);// true
- 展开运算符...
展开运算符是一个 es6 / es2015特性,它提供了一种非常方便的方式来执行浅拷贝,这与 Object.assign ()的功能相同。
let obj1 = { name: 'Kobe', address:{x:100,y:100}}
let obj2= {... obj1}
obj1.address.x = 200;
obj1.name = 'wade'
console.log('obj2',obj2) // obj2 { name: 'Kobe', address: { x: 200, y: 100 } }
- Array.prototype.concat()
let arr = [1, 3, {
username: 'kobe'
}];
let arr2 = arr.concat();
arr2[2].username = 'wade';
console.log(arr); //[ 1, 3, { username: 'wade' } ]
- Array.prototype.slice()
let arr = [1, 3, {
username: ' kobe'
}];
let arr3 = arr.slice();
arr3[2].username = 'wade'
console.log(arr); // [ 1, 3, { username: 'wade' } ]
深拷贝的实现方式
- JSON.parse(JSON.stringify())
利用JSON.stringify将对象转成JSON字符串,再用JSON.parse把字符串解析成对象,一去一来,新的对象产生了,而且对象会开辟新的栈,实现深拷贝。
这种方法虽然可以实现数组或对象深拷贝,但不能处理函数和正则,因为这两者基于JSON.stringify和JSON.parse处理后,得到的正则就不再是正则(变为空对象),得到的函数就不再是函数(变为null)了。
let arr = [1, 3, {
username: ' kobe'
},function(){}];
let arr4 = JSON.parse(JSON.stringify(arr));
arr4[2].username = 'duncan';
console.log(arr, arr4)
- 函数库lodash的_.cloneDeep方法
var _ = require('lodash');
var obj1 = {
a: 1,
b: { f: { g: 1 } },
c: [1, 2, 3]
};
var obj2 = _.cloneDeep(obj1);
console.log(obj1.b.f === obj2.b.f);// false
- jQuery.extend()方法
$.extend(deepCopy, target, object1, [objectN])//第一个参数为true,就是深拷贝
var $ = require('jquery');
var obj1 = {
a: 1,
b: { f: { g: 1 } },
c: [1, 2, 3]
};
var obj2 = $.extend(true, {}, obj1);
console.log(obj1.b.f === obj2.b.f); // false
【http篇】
1、osi七层模型
HTTP:是互联网上应用最为广泛的一种网络协议,是一个客户端和服务器端请求和应答的标准(TCP),用于从WWW服务器传输超文本到本地浏览器的传输协议,它可以使浏览器更加高效,使网络传输减少。
HTTPS:是以安全为目标的HTTP通道,简单讲是HTTP的安全版,即HTTP下加入SSL层,HTTPS的安全基础是SSL,因此加密的详细内容就需要SSL。
**http与https的区别 **
1、https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。
2、http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。 3、http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。 4、http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。
2、跨域问题
什么是跨域——blog.csdn.net/lambert310/…
跨域问题的解决——blog.csdn.net/lianzhang86…
!DOCTYPE含义——blog.csdn.net/sunhl951/ar…
3、http常用方法
获取资源的get方法,传输实体的post方法,传输文件的put方法,获取报文首部的head方法,删除文件的DELETE方法,OPTIONS: 用来询问服务器支持哪些方法。 www.php.cn/faq/416660.…
4、http常见状态码
2开头 (请求成功)表示成功处理了请求的状态代码。
3 开头 (请求被重定向)表示要完成请求,需要进一步操作。 通常,这些状态代码用来重定向。
301与302的区别:301表示永久性移动,302表示暂时性移动 blog.csdn.net/u010142437/…
4开头 (请求错误)这些状态代码表示请求可能出错,妨碍了服务器的处理。
5开头(服务器错误)这些状态代码表示服务器在尝试处理请求时发生内部错误。 这些错误可能是服务器本身的错误,而不是请求出错。
5、cookie、sessionStorage和localStorage区别
blog.csdn.net/weixin_4261…
www.cnblogs.com/beimingbing…
sessionStorage:仅在当前浏览器窗口关闭前有效,自然也就不可能持久保持;localStorage:始终有效,窗口或浏览器关闭也一直保存,因此用作持久数据;sessionStorage不在不同的浏览器窗口中共享,即使是同一个页面;localStorage 在所有同源窗口中都是共享的。
因此sessionStorage不是一种持久化的本地存储,仅仅是会话级别的存储。
而localStorage用于持久化的本地存储,除非主动删除数据,否则数据是永远不会过期的。
cookie——blog.csdn.net/zhangquan_z…
6、强缓存与协商缓存
blog.csdn.net/wsymcxy/art…
www.cnblogs.com/yayaxuping/…
强缓存——客户端不会向服务器发送请求,而是去查看缓存是否过期,如果没有过期则直接从缓存中读取资源;
协商缓存——向服务器发送请求,服务器会根据这个请求的request header的一些参数来判断是否命中协商缓存,如果命中,则返回304状态码并带上新的response header通知浏览器从缓存中读取资源;
7、https工作原理
8、对称加密与非对称加密
www.cnblogs.com/sunsky303/p…
对称加密指用同一个密钥进行加密和解密,安全性不高
非对称加密使用了一对密钥,公钥(public key)和私钥(private key)。私钥只能由一方安全保管,不能外泄,而公钥则可以发给任何请求它的人。非对称加密使用这对密钥中的一个进行加密,而解密则需要另一个密钥。
9、浏览器对象模型 (BOM, browserObjectModel)
一般来讲,在BOM中大致存在如下几个对象:
- window: 表示窗口对象
- Navigator:提供浏览器相关信息,包括浏览器名称、版本号、操作系统等等。
- location:浏览器中的地址栏,可以完成刷新当前页面与放回首页、显示端口号、服务器等信息。
- history:记录访问历史。返回上次访问地址:history.back。返回上上次history.back(2),以此类推。
- screen:提供用户屏幕相关信息
- document: 表示整个页面
DOM可以算是BOM的一个分支。因为BOM里面存在一个叫做Document的对象,但是这个对象的属性和方法太多了,所以W3C将其单独取了出来,做成了一套规范。
blog.csdn.net/qq_41908550…
【es6】
es6箭头函数的this指向问题
es6由于箭头函数this是在定义函数时绑定的,不是在执行过程中绑定的,它会捕获其所在(即定义的位置)上下文的this值, 作为自己的this值.
es6箭头函数没有自己的this es6箭头函数里的this是外层代码(定义时,非执行时)this的引用