一、es6中的箭头函数和普通函数有什么区别?
1、普通函数中的 this总是指向调用它的那个对象,
箭头函数没有自己的this,他的this永远指向其定义环境,任何方法都改变不了其指向,如call()、bind()、apply()。(正是因为它没有this,所以也就不能用作构造函数,也没有原型对象)
箭头函数不能当作构造函数,也就是说,不能使用new命令,否则会报错。
箭头函数没有原型属性。
箭头函数不可以使用yield命令,因此箭头函数不能用作Generator函数。
箭头函数不能使用arguments对象,该对象在函数体内不存在。如果要用,可以用rest参数代替。
变量提升:由于js的内存机制,function的级别最高,而用箭头函数定义函数的时候,需要var(let、const)关键字,而var所定义的变量不能得到变量提升。故箭头函数一定要定义于调用之前。
拓展:this的指向问题?
1、普通函数中,this指向其函数的直接调用者;
2、箭头函数中,this指向其定义环境,任何方法都改变不了其指向,如call( )、bind()等;
3、构造函数中,如果不使用new,则this指向window,
如果使用new创建了一个实例,则this指向该实例。
4、window内置函数中,如setInterval,setTimeout等,其内部的this指向Window。
5、匿名函数的this指向Window。
6、apply()、call()、bind()可以改变this的指向
二、谈谈你对原型链的认识?
https://blog.csdn.net/xiaotao_css/article/details/72782416:通俗易懂的介绍(仅供18岁以上成年阅读)
看下面的东西之前,建议先看上面链接的这篇文章
对象: 1 、 函数对象: 由 function 创造出来的函数,比如 function a(){ } ; 系统内置的函数对象: Function , Object , Array , String , Number
2、 普通对象: 除开函数对象之外的对象,都是普通对象
每个普通对象的__proto__属性,都指向Object().prototype ,
var obj = { } 就等于 var obj = new Object ( ) ; 即普通对象是 构造函数( Object ) 的一个实例
所以 obj. __proto__ === Object.prototype ( 但是老高说的,Object的原型还是object对象, )
obj. constructor === Object
凡是通过 new Function() 创建的对象都是函数对象,其他的都是普通对象。
注:所有对象都有 __proto__ 属性只有函数对象才有 prototype 属性 !!!!!!!!!!!
原型对象: prototype 属性也叫原型对象,主要是为了实现继承;
指针 __proto__ : js中,万物皆对象!所有的对象 obj 都具有 proto 属性(null 和 undefined除外 ),而且指向创造obj 对象的函数对象(生成实例的构造函数)的prototype属性 。 如以下例子:
Person 构造函数的原型对象 是 Mother ()。相当于原型是妈妈,Person现在是儿子。
在 p1 和 p2 实例中,__proto__属性,指向的是 创造他们的构造函数Person 对象的 prototype 属性,所对应的对象。
也就是 Mother().
一个构造函数对象的原型,就相当于 他妈,这个构造函数对象的实例,就相当于 他妈不同的孩子。
而 每个 实例中的 __proto__属性,就指向 他们共同的 妈 !也就是 构造函数对象的 prototype属性。
当我们输入 p1.name 的时候,原型链的搜索机制是先在实例中搜索相应的值,找不到就通过它的__proto__指针,在原型中找,还找不到就再往上一级原型中搜索……一直到了原型链的终点( 就是js自带的Object,它的原型比较特殊,为null ),就是到null还没找到的话,就返回一个 undefined。
构造器constructor : 每一个对象中的constructor 属性返回创建此对象的函数对象的引用;例如:
functon Dog ( name, color ) {
this. name = name;
this. color = color;
}
var dog1 = new Dog( "小白" , "白色" );
dog1.constructor == Dog 构造函数本身
在默认情况下,所有的原型对象都会自动获得一个 constructor(构造函数)属性,这个属性(是一个指针)指向 prototype 属性所在的函数(Person)
上面这句话有点拗口,我们「翻译」一下:A 有一个默认的 constructor 属性,这个属性是一个指针,指向 Person。即:
Person.prototype.constructor == Person
实例的构造函数属性(constructor)指向构造函数 :person1.constructor == Person
person1 为什么有 constructor 属性?那是因为 person1 是 Person 的实例。
那 Person.prototype 为什么有 constructor 属性??同理, Person.prototype (你把它想象成 A) 也是Person 的实例。
也就是在 Person 创建的时候,创建了一个它的实例对象并赋值给它的 prototype,基本过程如下:
var A = new Person();
Person.prototype = A;
结论:原型对象(Person.prototype)是 构造函数(Person)的一个实例。
以下代码的图示
function Foo ( ) { } ;
var f1 = new Foo;
以下是 深入解答原型是怎么回事 的一篇文章
1、https://www.jianshu.com/p/dee9f8b14771
2、https://www.jianshu.com/p/652991a67186
3、https://www.jianshu.com/p/a4e1e7b6f4f8
原型链的概念
如果问原型链是什么,直接把下面的这张图画出来就行了。
https://www.cnblogs.com/shuiyi/p/5305435.html
腾讯大学原生JS 视频 :https://ke.qq.com/course/231577
回答出以下问题,就知道 __proto__和prototype 的什么了
function Person(){ } ;
var person1 = new Person( );
person1.__proto__ 是什么?
Person.__proto__ 是什么?
Person.prototype.__proto__ 是什么?
Object.__proto__ 是什么?
Object.prototype__proto__ 是什么?
答案:
第一题:
因为 person1.__proto__ === person1 的构造函数.prototype
因为 person1的构造函数 === Person
所以person1.__proto__ === Person.prototype
第二题:
因为 Person.__proto__ === Person的构造函数.prototype
因为 Person的构造函数 === Function
所以Person.__proto__ === Function.prototype
第三题:
Person.prototype 是一个普通对象,我们无需关注它有哪些属性,只要记住它是一个普通对象。
因为一个普通对象的构造函数 === Object
所以Person.prototype.__proto__ === Object.prototype
第四题,参照第二题,因为 Person 和 Object 一样都是构造函数
不要把 Object 想的太复杂,它其实也不过是 Function 构造出来的一个 方法 (一个普通的构造函数)
所以 Object.__proto__ === Function.prototype
第五题:
Object.prototype 对象也有proto属性,但它比较特殊,为 null 。因为 null 处于原型链的顶端,这个只能记住。
Object.prototype.__proto__ === null
记住:prototype是一个普通对象,所有普通对象的__protp__都指向 Object.prototype
三、js跨域问题怎么解决?
1、 JSONP跨域请求
要理解跨域,先要了解一下”同源策略“。所谓同源是指,协议、域名、端口都相同。所谓”同源策略“,简单的说,就是基于安全考虑,当前域不能访问其他域的东西。
http 和 https :协议不同
www.a.com : 8080 和www.a.com : 1000 : 端口不同
在同源策略下,在某个服务器下的页面是无法获取到该服务器以外的数据的。
例如我们在自己的网站通过 ajax 去 获取豆瓣上的图书接口:
https://api.douban.com/v2/book/search?q=javascript&count=1
我们通过以上 ajax 去访问,发现运行时会报错:
只要出现这个错误,就说明服务器接口不支持跨域
//No 'Access-Control-Allow-Origin' header is present on the requested resource
这是因为不同源,所以无法访问其他服务器的数据
但是<img> 的 src ( 获取图片 ) , <link> 的 href (获取css),<script> 的 src (获取js)这三个属性都不符合同源策略,它们可以跨域获取数据。JSONP就是利用<script> 的 src 来实现跨域获取数据。
跨域原理
JSONP实现跨域请求的原理,简单的说,就说动态创建 <script>标签,然后利用<script>的 src 不受同源策略的约束来跨域获取数据。
JSONP 由两部分组成:回调函数和数据。回调函数 是当响应到来时,应该在页面中调用的函数。回调函数的名字,一般是在请求中指定的。而数据就是传入回调函数中的 JSON 数据。
注意:JSONP不是真正的 ajax
ajax是异步的,jsonp是同步的,所以它不是真正的ajax
动态创建 <script> 标签,设置其 src ,回调函数在 src 中设置:
var script = document.createElement("script");
script.src = "https://api.douban.com/v2/book/search?q=javascript&count=1";
document.body.insertBefore( script, document.body.firstChild );
在页面中,返回的 JSON 作为参数传入 回调函数中,我们通过回调函数来 操作数据
function handleResponse(response){
//对 response 数据进行操作代码
}
了解了 JSONP 的基本使用方法,我们在实现上面,通过 ajax 调用豆瓣接口的需求,实现代码如下:
注意:以上代码中,要记得成功访问完数据后,要删除创建的动态 <script>标签:
document.body.removeChild(script)
其实在接口数据的形式类似:fn( { name: "张三" , age: "20" } ) ,我们传递过去一个和这个函数名字相同的回调函数,参数就是访问到的数据。
假如接口是:http://api.money.126.net/data/feed/0000001,1399001?callback=refreshPrice
函数名就是 callbanck = 的值:refreshPrice.
JSONP目前还是比较流行的跨域方式,虽然JSONP使用起来方便,但是也存在一些问题:如果其他域不安全,很可能会在响应中夹带一些恶意代码。而且要确定 JSONP请求是否失败并不容易。
JSONP有个限制,只能用GET请求,并且要求返回JavaScript
更多跨域的方法介绍:
对axios有更进一步的理解,利用cros进行跨域处理!!!
2、CROS跨域
3、反向代理
四、闭包
概念: 闭包就是能够读取其他函数内部变量的函数。
由于函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成“定义在一个函数内部的函数”(然后将这个内部的函数 return 返回出来)。
所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。
闭包的作用:它最大的用处有两个: 1) 一个是可以读取函数内部的变量;
2) 另一个就是让这些变量的值始终保存在内存中。
使用闭包的注意点:1) 由于闭包会使得函数中的变量都保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄漏。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
内存泄漏:程序的运行需要内存。对于持续运行的服务进程,必须及时释放不再用到的内存,否则占用越来越高,轻则影响系统性能,重则导致进程崩溃。不再用到的内存,没有及时释放,就叫做内存泄漏。
http://www.ruanyifeng.com/blog/2017/04/memory-leak.html
2) 闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法,把内部变量当作它的私有属性,这时一定要小心,不要随便改变父函数内部变量的值。
五、promise的用法和原理?
基础https://mengera88.github.io/2017/05/15/promise%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/
原理
https://segmentfault.com/a/1190000009478377
概念:promise 是异步编程的一种解决方案。它可以把异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。
Promise 为异步操作提供了统一的接口,使得控制异步操作更加容易,它的强大之处在于它的链式调用。
基本用法:
newPromise(function(resolve, reject){
//待处理的异步逻辑
//处理结束后,调用resolve或reject方法
})
新建一个promise很简单,只需要new 一个 Promise 对象即可。所以promise本质上就是一个函数,它接受一个函数作为参数,并且返回promise对象,这就给链式调用提供了基础。
特点:
1、对象的状态不受外界影响。
Promise 的实例 有以下三种状态:
1)pending : 进行中
2)resolved : 已成功完成
3)rejected : 已失败
只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。
2、一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为resolved;从pending变为rejected。 只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。
基本用法:
ES6规定,Promise 对象是一个构造函数,用来生成Promise实例
Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。它们是两个函数,由JavaScript 引擎提供, 不是自己部署。
resolve函数的作用,将Promise对象的状态从“进行中”变成 “成功”( 即从pending变为resolved ) ,在异步操作成功时调用,并将异步操作的结果,作为参数传递出去。
reject函数的作用,在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
Promise实例生成以后,可以用then方法分别制定 Resolved状态和Rejected状态的回调函数:
then方法可以接受 2 个回调函数作为参数,第二个函数是可选的,不一定要提供。这两个函数都接受Promise对象传出的值作为参数。
promise捕获错误 .catch方法:
Promise.prototype.catch方法是Promise.prototype.then(null, rejection)的别名,用于指定发生错误时的回调函数。
Promise对象的错误具有“ 冒泡 ”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获。
题外话:async 函数是es7 提案出来的语法, async函数是用来取代回调函数的另一种方法。