美文网首页
前端面试知识点(JavaScript篇-进阶)

前端面试知识点(JavaScript篇-进阶)

作者: 何小鹏 | 来源:发表于2018-06-27 14:58 被阅读209次
1.原型链

每个对象都会在其内部初始化一个属性,就是prototype(原型)

当我们访问一个对象的属性时,
如果这个对象内部不存在这个属性,那么他就会去prototype里找这个属性,这个prototype又会有自己的prototype, 于是就这样一直找下去,也就是我们平时所说的原型链的概念。

image
关系:instance.constructor.prototype = instance.-proto-
image

特点: JavaScript对象是通过引用来传递的,我们创建的每个新对象实体中并没有一份属于自己的原型副本。当我们修改原型时,与之相关的对象也会继承这一改变。


image
2.闭包

闭包是指有权访问另一个函数作用域中变量的函数,创建闭包的最常见的方式就是在一个函数内创建另一个函数,通过另一个函数访问这个函数的局部变量,利用闭包可以突破作用链域,将函数内部的变量和方法传递到外部。
闭包的优点

  1. 创建一个匿名函数,并立即执行,加强了安全性和封装性,由于外部无法引用内部变量,不会造成全局污染。
  2. 缓存,可以将值存储起来,当需要调用的时候,首先在缓存中查找,如果找不 到再进行计算,然后更新缓存并返回值
  3. 实现封装和实现面向对象中的对象。

闭包的缺点

  1. 闭包会导致变量不能释放,引起内存泄露。

示例

   function say667() {
    // Local variable that ends up within closure
    var num = 666;
    var sayAlert = function() {
        alert(num);
    }
    num++;
    return sayAlert;
 }

  var sayAlert = say667();
  sayAlert()//执行结果应该弹出的667

执行say667()后,say667()闭包内部变量会存在,而闭包内部函数的内部变量不会存在
使得Javascript的垃圾回收机制GC不会收回say667()所占用的资源
因为say667()的内部函数的执行需要依赖say667()中的变量
这是对闭包作用的非常直白的描述

3.作用域

变量的作用域无非就是两种:全局变量和局部变量
全局作用域
最外层函数定义的变量拥有全局作用域,即对任何内部函数来说,都是可以访问的:

     var outerVar = "outer";
      function fn(){
         console.log(outerVar);
      }
      fn();//result:outer

局部作用域
和全局作用域相反,局部作用域一般只在固定的代码片段内可访问到,而对于函数外部是无法访问的,最常见的例如函数内部

<script>
      function fn(){
         var innerVar = "inner";
      }
      fn();
      console.log(innerVar);// ReferenceError: innerVar is not defined
</script>

作用域的特点
次序性和向上线程性搜索

4、Ajax请求(jQuery方式)

定义
无需重新加载整个网页的情况下,能够更新部分网页的技术
ajax的全称:Asynchronous Javascript And XML。(异步传输+js+xml)
请求方式
$.ajax()返回其创建的 XMLHttpRequest 对象。

$.ajax({
    type: "get",
    dataType: "application/json",
    url: '/Ajax.json', 
    success: function (data) {
        }
});

==在ajax请求中type的值分为(GET、POST、DELETE)最常见的==
GET:获取服务端数据,用get方式可传送简单数据,但大小一般限制在1KB下,数据追加到url中发送(http的header传送)。安全性能高。
POST:上传数据到服务端,数据上传无大小限制,但是需要设置content—type请求头;
DELETE:用于删除数据。
加载方式

  1. 同步:单线程运行,发送方发出数据后,等接收方发回响应以后才发下一个数据包的通讯方式。用户填写所有信息后,提交给服务器,等待服务器的回应(检验数据),是一次性的。信息错误又要重新填写!
    客户端请求(等待)->服务端处理->响应->页面载入

  2. 异步:发送方发出数据后,不等接收方发回响应,接着发送下个数据包的通讯方式。

页面上的操作和服务器端的操作互相之间不会造成阻塞
(1)new一个XHR对象
(2)调用open方法
(3)send一些数据
(4)对过程进行监听,来知道服务器是不是正确地做出了响应,接着可以做一些事情

跨域解决方式

  1. jsonp、
  2. iframe、
  3. window.name、
  4. window.postMessage、
  5. 服务器上设置代理页面(CORS)
5. 数据拷贝的方法

1、数据相互赋值实现拷贝

let a=[0,1,2,3,4],
    b=a;
console.log(a===b);
b[0]=1;
console.log(a,b);

在这里打印a和b的时候,我们明明b复制了a,但修改数组a,数组b也跟着变了。而当我们a[0]=1时进行数组修改时,由于a与b指向的是同一个地址,所以自然b也受了影响,这就是所谓的浅拷贝了。

浅拷贝出现地址引用的原因

而对于对象这种内存占用比较大的来说,直接让复制的东西等于要复制的,那么就会发生引用,因为这种复制,只是将复制出来的东西的指向指向了要复制的那个东西,简单的说,就是两个都同时指向了一个空间,如果改变其中一个,另一个也会发生变化。这就发生了引用。

** 引用数据类型--名存在栈内存中,值存在于堆内存中,但是栈内存会提供一个引用的地址指向堆内存中的值**

image

2.对象使用递归实现拷贝

function deepClone(obj){
    let objClone = Array.isArray(obj)?[]:{};
    if(obj && typeof obj==="object"){
        for(key in obj){
            if(obj.hasOwnProperty(key)){
                //判断ojb子元素是否为对象,如果是,递归复制
                if(obj[key]&&typeof obj[key] ==="object"){
                    objClone[key] = deepClone(obj[key]);
                }else{
                    //如果不是,简单复制
                    objClone[key] = obj[key];
                }
            }
        }
    }
    return objClone;
}    
let a=[1,2,3,4],
    b=deepClone(a);
a[0]=2;
console.log(a,b);

递归递归去复制所有层级属性的拷贝方式,是开辟一个新的内存专门为b存放值,这样就实现了深拷贝
在ES6中的,对于数组的拷贝复制又有新的方式

  1. Array.from(要复制的数组):
var arr1=[1,2,3];
var arr2=Array.from(arr1);
arr1.push(4);
alert(arr1);  //1234
alert(arr2);  //123
arr2.push(5);
alert(arr1);  //1234
alert(arr2);  //1235 
  1. 对象转字符串再将其转回对象的方式
let a =[{a:1}]
let b = JSON.parse(JSON.stringify(a))
b[0].a=3

consloe.log(a[0].a)  // 1

用json转才可以完成深赋值,因为字符串转成对象之后就是一个新的对象了;

  1. ...方式
var arr1=[1,2,3];
var arr2=[...arr1];
arr1.push(4);
alert(arr1);  //1234
alert(arr2);  //123
arr2.push(5);
alert(arr1);  //1234
alert(arr2);  //1235
6. 数组去重方式

1. 循环对比
双层循环,外层循环元素,内层循环时比较值,相同的值则跳过,不相同则push进数组

Array.prototype.distinct = function(){
 var arr = this,
  result = [],
  i,
  j,
  len = arr.length;
 for(i = 0; i < len; i++){
  for(j = i + 1; j < len; j++){
   if(arr[i] === arr[j]){
    j = ++i;
   }
  }
  result.push(arr[i]);
 }
 return result;
}
var arra = [1,2,3,4,4,1,1,2,1,1,1];
arra.distinct();    //返回[3,4,2,1]

2. 属性对比去重
利用对象的属性不能相同的特点进行去重

Array.prototype.distinct = function (){
 var arr = this,
  i,
  obj = {},
  result = [],
  len = arr.length;
 for(i = 0; i< arr.length; i++){
  if(!obj[arr[i]]){ //如果能查找到,证明数组元素重复了
   obj[arr[i]] = 1;
   result.push(arr[i]);
  }
 }
 return result;
};
var a = [1,2,3,4,5,6,5,3,2,4,56,4,1,2,1,1,1,1,1,1,];
var b = a.distinct();
console.log(b.toString()); //1,2,3,4,5,6,56

3. 利用ES6的set
Set数据结构,它类似于数组,其成员的值都是唯一的。

function dedupe(array){
 return Array.from(new Set(array));
}
dedupe([1,1,2,3]) //[1,2,3]
7.call和apply的使用

call和apply,它们的作用都是将函数绑定到另外一个对象上去运行

/*apply()方法*/
function.apply(thisObj[, argArray]);

/*call()方法*/
function.call(thisObj[, arg1[, arg2[, [,...argN]]]]);

它们各自的定义

apply:调用一个对象的一个方法,用另一个对象替换当前对象。例如:B.apply(A, arguments);即A对象应用B对象的方法。

call:调用一个对象的一个方法,用另一个对象替换当前对象。例如:B.call(A, args1,args2);即A对象调用B对象的方法。

它们的共同之处

都“可以用来代替另一个对象调用一个方法,将一个函数的对象上下文从初始的上下文改变为由thisObj指定的新对象”。

它们的不同之处

apply:最多只能有两个参数——新this对象和一个数组argArray。如果给该方法传递多个参数,则把参数都写进这个数组里面,当然,即使只有一个参数,也要写进数组里。如果argArray不是一个有效的数组或arguments对象,那么将导致一个TypeError。如果没有提供argArray和thisObj任何一个参数,那么Global对象将被用作thisObj,并且无法被传递任何参数。

call:它可以接受多个参数,第一个参数与apply一样,后面则是一串参数列表。这个方法主要用在js对象各方法相互调用的时候,使当前this实例指针保持一致,或者在特殊情况下需要改变this指针。如果没有提供thisObj参数,那么 Global 对象被用作thisObj。

相关文章

网友评论

      本文标题:前端面试知识点(JavaScript篇-进阶)

      本文链接:https://www.haomeiwen.com/subject/ykxbyftx.html