js

JS 有哪些数据类型?

7种数据类型 string number bool undefined null object symbol

object 包括了数组、函数、正则、日期等对象 一旦出现(数组、函数、正则、日期、NaN)直接0分

(必考)什么是立即执行函数?使用立即执行函数的目的是什么?

声明一个函数,不给他名字(也可以给)然后在后面加上括号,立即执行他,由于这个语法不对,他不能立即执行,所以修改一下语法,让他立即执行,比如用括号包起来,前面加一个!,或者~

;(function (){
     var name
 }())
 //为了防止括号和前面的相作用,就添加一个分号,以示分离
 ;(function (){
     var name
 })()
 !!!!!!!function (){
     var name
 }()
 ~function (){
     var name
 }()
复制代码

作用: 造出一个函数作用域,防止污染全局变量 侃侃而谈 ES 6 有一个新语法,可以直接声明局部变量,花括号{}就代替了立即执行函数。

 {
     let  name
 }
复制代码

JS 如何实现继承?

举例子写出来:(要理解下面每句话的意思)

  1. 原型链
function Animal(){
      this.body = '肉体'
  }
  Animal.prototype.move = function(){
  }
复制代码

下面写一个Human继承animal

  function Human(name){
      Animal.apply(this, arguments)//自身属性的继承
      this.name = name
  }
  // Human.prototype.__proto__ = Animal.prototype // 非法,不被支持
	//所以得生成一个空函数,下面这三句话就是上面这句话的意思
  var f = function(){}
  f.prototype = Animal.prototype
  Human.prototype = new f()
//然后再写Human自己的方法。这个时候我们就说Human继承了animal
  Human.prototype.useTools = function(){}
复制代码

var frank = new Human()

  1. extends 关键字
class Animal{
      constructor(){
          this.body = '肉体'
      },
      move(){}//这里就不实现了
  }
  class Human extends Animal{
      constructor(name){
          super()
          this.name = name
      },
      useTools(){}
  }
  var frank = new Human()
复制代码

如果只记住下面这种没有问题,能记住原型链的更好,class语法知识语法糖。

移动端的触摸事件了解吗?

  1. 讲一下这几个事件: touchstart touchmove touchend touchcancel
  2. 模拟 swipe 事件:记录两次 touchmove 的位置差,如果后一次在前一次的右边,说明向右滑了。

JS

&&和||

&&,他的返回值就是遇到的第一个falsy值,后面的不看了 1&&2&&3&&0&&2&&3 返回0,后面的不运行。 ||,他返回的就是第一个遇到的true值,后面的不看 0||0||0||false||1||2||0返回1,后面的不运行。

七个数据类型

七种数据类型分为六个基本类型和对象。 六个基本类型分别是nullundefinedstringnumberbooleansymbol 对象又分为objectfunctionarray

null和undefined的区别

相同点:if里都是都是false值

不同点:

  1. null转换为数字的时候是0undefined表示未定义转换为数字的时候是NaN

  2. 调用函数时,某个参数未设置任何值,这时就可以传入null,表示该参数为空。而传入undefined就代表undefined

typeof

yuchengkai.cn/docs/zh/fro… typeof只需要记住两点

  • typeof 对于基本类型,除了 null 显示为 object,其余都可以显示正确的类型
  • typeof 对于对象,除了函数为function都会显示object
  • typeof运算符只能显示数组的类型是Object,而Array.isArray(arr)方法可以识别数组

六个false值

除了下面六个值被转为false,其他值都视为true

undefined
null
false
0
NaN
""''(空字符串)
复制代码

NaN

NaN === NaN;        // false
typeof NaN//number
复制代码

for in 和forEach

for in 遍历对象

for in循环注意点

for(var key in person){
    console.log(person.key)
}
复制代码

注意:person.key 等于 person['key'] 要用 这里遍历要用person[key]

for... in可能会随机遍历,不按照声明顺序

forEach遍历数组

var arr=[1,2,3]
arr.forEach(function(e){
	console.log(e)
})
//1  2   3

复制代码

其他类型转换为String

  • nullundefined没有tostring()方法。只能null+''undefined+''
  • 其他的用.toString()方法。或直接+''。或者window.String()方法

其他类型转换为Number(主要是String)

五个方法

  • 简便方法'1234'-0(常用)
  • .parseInt()//默认十进制
  • 简便方法二+'1'取正

其他类型转换为Boolean(五个false值)

记住5个false值 null undefined NaN 0 ''

记住下面易错点:

[]//ture
{}//ture
' '//ture 里面有空格
复制代码

转换方法两个:

  • window.Boolean(xxx)
  • 前面加上!!xxx

内存

  • 数据区(存的变量的数据)分为 Stack(栈内存) 和 Heap(堆内存)
  • 简单类型的数据直接存在 Stack 里
  • 复杂类型的数据是把 Heap 地址存在 Stack 里(对象包括:狭义对象(object),函数(function),数组(array))

关于内存的面试题

引用: 一个广义对象(object,array,function) 例如 var a= {xxx:'xxx'} a存的是{xxx:'xxx'}这个广义对象的地址,假如是10000。那么a(或a存的地址10000)就是这个对象({xxx:'xxx'})的引用

var a = 1 var b = a b = 2 请问 a 显示是几? 2 1 (深拷贝)

var a = {name: 'a'}
var b = a
b = {name: 'b'}
复制代码

请问现在 a.name 是多少? 'a'

var a = {name: 'a'}
var b = a
b.name = 'b'
复制代码

请问现在 a.name 是多少? 'b'

var a = {name: 'a'}
var b = a
b = null
复制代码

请问现在 a 是什么? {name: 'a'}

GC垃圾回收

如果一个对象没有被引用,他就是垃圾,就会被回收(没有引用找不到他,所以要回收)

{name:'a'}就是垃圾,就要被 回收释放内存
不会回收,因为 document.body.onclick这个 引用在栈内存中存了fu这个函数的 地址

内存泄漏:在ie6的时候,如果关闭页面,一些垃圾是没有被清除,内存被永久的占用了

深拷贝

  • 首先:值的存储方法:两种。基础类型六个存入stack****栈内存,复合类型对象存入heap****堆内存

所以:

  • 对于六个基本数据类型来说,直接赋值就是深拷贝,因为他们直接存在栈内存里。
  • 但是如果吧旧的对象赋值给新声明的对象,那么实际上只是把栈内存里的地址赋值给了新对象,实际上堆内存里仍然是原来的对象,如果修改新对象里面的属性,那么原来的对象也会改变,因为用的是同一块堆内存。直接给新对象赋值,无法实现拷贝。

如何实现对象的浅与深拷贝?

  1. 对象浅拷贝
  • 浅拷贝 首先可以通过Object.assign 来解决这个问题。
    
    let a = {
        age: 1
    }
    let b = Object.assign({}, a)
    a.age = 2
    console.log(b.age) // 1
    复制代码
    Object.assign()只拷贝第一层,如果属性存的是一个引用,那么他也只拷贝到引用。所以是浅拷贝
  • 深拷贝 深拷贝 这个问题通常可以通过JSON.parse(JSON.stringify(object))来解决。
let a = {
    age: 1,
    jobs: {
        first: 'FE'
    }
}
let b = JSON.parse(JSON.stringify(a))
a.jobs.first = 'native'
console.log(b.jobs.first) // FE
复制代码

但是该方法也是有局限性的:

  • 会忽略 undefined
  • 不能序列化函数
  • 不能解决循环引用的对象 但是可以解决大部分问题。

作用域

JavaScript有函数级作用域,仅仅函数能创建新作用域。

变量的作用域表示这个变量存在的上下文

setTimeout中的函数所处在于全局作用域中,所以函数中使用this关键字时,这个this关键字指向的是全局对象window

作用域链

题目:

var a = 1
function f1(){
  var a = 2
  console.log(a)
  f4()
  
}
function f4(){
  console.log(a)
}

f1()
复制代码

答案,2,1 这个f4里面的a只能是他自己本身的作用域他的父作用域,跟f1里面的a没有关系 先看面试题 题目1

var a = 1
function fn1(){
  function fn2(){
    console.log(a)
  }
  function fn3(){
    var a = 4
    fn2()
  }
  var a = 2
  return fn3
}
var fn = fn1()
fn() //2
复制代码

题目2

var a = 1
function fn1(){
  function fn3(){
    var a = 4
    fn2()
  }
  var a = 2
  return fn3
}
function fn2(){
  console.log(a)
}
var fn = fn1()
fn() //1
复制代码

题目3

var a = 1
function fn1(){

  function fn3(){
    function fn2(){
      console.log(a)
    }
    var a

    fn2()
    a = 4
  }
  var a = 2
  return fn3
}
var fn = fn1()
fn() //undefined
复制代码

解密

  1. 函数在执行的过程中,先从自己内部找变量
  2. 如果找不到,再从创建当前函数所在的作用域去找, 以此往上
  3. 注意找的是变量的当前的状态

变量提升

两个题目

var foo = 1;
function bar() {
    if (!foo) {
        var foo = 10;
    }
    alert(foo);
}
bar();
复制代码

答案是10

第二个例子

var a = 1;
function b() {
    a = 10;
    return;
    function a() {}
}
b();
alert(a);
复制代码

alert输出了1

变量提升后的顺序 提升时代码表现上的排序为:

1 var a;
2 function f (){}
3 形参
4 thisarguments
复制代码

总结: var 声明变量,只会提升var a=undefined(自动赋值为undefined)。 function声明函数,function f (){},所有的都会提升。

let与const暂时死区

{ console.log(x) // Uncaught ReferenceError: x is not defined let x = 1 } let x = 1之前到花括号{,就是x的暂时死区,不用使用变量x,const一样

instanceof

instanceof运算符用来验证,一个对象是否为指定的构造函数实例obj instanceof Object返回true,就表示obj对象是Object的实例。

new

  1. 使用new命令时,它后面的函数依次执行下面的步骤。

    1. 创建一个空对象,作为将要返回的对象实例。
    2. 将这个空对象的原型,指向构造函数的prototype属性。
    3. 将这个空对象赋值给函数内部的this关键字。
    4. 开始执行构造函数内部的代码。

既:

  1. 新生成了一个对象(新生成一个临时对象)
  2. 链接到原型(新生成的对象 新对象.__proto__=构造函数.prototype
  3. 绑定 this ( this = 新对象)
  4. 返回新对象 new应用举例:

第一步写私有属性,第二步写共有属性.

可以看到这个对象的

1自有属性 2__proto__指向的原型对象含有共有属性. 3 共共有属性(原型的属性)constructor指向的构造函数

ES5的Object.create()

现有的对象作为模板,生成实例对象,这时就可以使用Object.create()方法。

var person1 = {
  name: '张三',
  age: 38,
  greeting: function() {
    console.log('Hi! I\'m ' + this.name + '.');
  }
};

var person2 = Object.create(person1);

person2.name // 张三
person2.greeting() // Hi! I'm 张三.
复制代码

this,this的实质:

  • 引擎会将函数单独保存在内存中,然后再将函数的地址赋值给变量。
  • 由于函数是一个单独的值,所以它可以在不同的**环境(上下文)**执行。
  • JavaScript 允许在函数体内部,引用当前环境的其他变量。
  • 现在问题就来了,由于函数可以在不同的运行环境执行,所以需要有一种机制,能够在函数体内部获得当前的运行环境(context)。所以,this就出现了,它的设计目的就是在函数体内部,指代函数当前的运行环境

总结一句话:this就是指函数所在的当前环境(既所在的对象)

this:

  • 在构造函数里,this 代表新生成的实例对象
  • 其他情况下,this就是属性方法当前”所在的对象。它总是返回一个对象

面试回答:;

  1. fn() 里面的 this 就是 window(fn在最顶层)
  2. fn()strict modethis 就是 undefined(fn在最顶层,严格模式下是undefined)
  3. a.b.c.fn() 里面的 this 就是 a.b.c(属性或方法当前所在的对象。)
  4. new F() 里面的 this 就是新生成的实例
  5. () => console.log(this) 里面 this 跟外面的 this 的值一模一样(ES6新增语法)(箭头函数里,this就上一层的this的值) 面试答上面的几个↑

面试回答call() apply() bind():

1 call()方法,可以指定函数内部this指向(即函数执行时所在的作用域),然后在所指定的作用域中调用该函数。 (这个所指定的作用域意思就是所指定的对象)

var obj = {};

var f = function () {
  return this;
};

f() === window // true
f.call(obj) === obj // true
复制代码

call后面的参数则是函数调用时所需的参数

2 apply()call()唯一的区别就是,它接收一个数组作为函数执行时的参数。

    x.call(obj,1,2,3)
    x.apply(obj,[1,2,3])
复制代码

3 bind方法用于将函数体内的this绑定到某个对象,然后返回一个新函数callapply都是绑定新this直接执行函数,而bind没有执行,而是返回新函数,这个在一函数作为参数的一些方法里就用bind

this面试题 带call的面试题遵循以下两个规则

  1. 如果call传了参数,this就是传的参数。

  2. 如果call没传参数或者为undefined,那么this是window(飞严格模式),或者undefined(严格模式)

带call的面试题仍然遵循规则 3.

4.

记住了,当call()的第一个参数是undefined的时候, thiswindow.

this面试题 this题目

答案: 调用B处的console.log().结果是options window(console.log()中console是全局window对象里的一个方法)

第二题:

答案:D Object

第三题:

答案:Object

JS 原型是什么?

举例

  1. var a = [1,2,3]
  2. 只有0、1、2、length 4 个key
  3. 但是a可以a.push(4) pop 这些方法
  4. a.__proto__ === Array.prototype(a是实例数组对象,Array是构造函数)
  5. push函数 就是沿着 a.__proto__ 找到的,也就是 Array.prototype.push
  6. Array.prototype 还有很多方法,如joinpopslicespliceconcat
  7. Array.prototype 就是 a 的原型(proto)

聚完例子后用new对象举例,说给面试官听: 比若说

  1. 我们新创建一个构造函数

    function Person() {}
    复制代码
  2. 然后根据构造函数构造一个新对象

     var person1 = new Person();
     ```
    
    复制代码
  3. 每个函数都有一个 prototype 属性,这个构造函数prototype 属性指向了一个对象,这个对象调用该构造函数而创建的实例原型

  4. 当我们给Personprototypename属性赋值为'Kevin'

    Person.prototype.name = 'Kevin';
    var person1 = new Person();
    var person2 = new Person();
    console.log(person1.name) // Kevin
    console.log(person2.name) // Kevin
    复制代码

    每一个新的实例对象对象都会从原型"继承"属性,实例对象拥有该原型的所有属性。

  5. 说白了,原型就是 构造函数 用来 构造 新实例 的 模板对象。

  6. 这就是原型。

什么是原型链?

先回答什么是原型。在上面,然后继续从__proto__开始往下说。

说:

  1. JavaScript对象除了 null 都具有的一个属性,叫__proto__,这个属性会指向该对象的原型对象。

  2. 当读取实例的属性时,如果找不到,就会通过__proto__查找原型中的属性,如果还查不到,就去找原型原型

  3. 例如Person.prototype这个原型的原型就是Object这个构造函数的prototype,既Object.prototype这个原型对象。然后,Person.prototype.__proto__就指向Object.prototype这个原型。然后Object.prototype原型是null

  4. 这些原型对象通过__proto__像链子一样连起来,就叫做原型链。 然后给面试官画:

链子上都画上__proto__ person1----->Person.prototype----->Object.prototype----->null

Array.prototype----->Object.prototype----->null

实例的.__proto__ 指向构造函数. prototype(原型对象) 面试题:

Number.prototype.__proto__ === Object.prototype
//true
String.prototype.__proto__ === Object.prototype
//true
Boolean.prototype.__proto__ === Object.prototype
//true
Array.prototype.__proto__ === Object.prototype
复制代码
//同理
Function.prototype.__proto__=== Object.prototype//true
复制代码

关于 Function只需记住下面: 构造函数的原型:

String.__proto__===Function.prototype//true
Number.__proto__===Function.prototype//true
Boolean.__proto__===Function.prototype//true

Object.__proto__ === Function.prototype//true
复制代码

特殊的:

Function.__proto__===Function.Prototype//true
复制代码

constructor

每一个构造函数的prototype属性都指向原型对象。 每一个原型对象的constructor属性都指向构造函数。 原型对象

    console.log(Person === Person.prototype.constructor); // true
复制代码

Array注意点、伪数组

  • 大BUG:var a = Array(3) 一个参数,且参数为数字,那么久声明数组长度为3的空数组

    var a = Array(3,3) 两个参数以上的时候,里面的参数都是数组内部的值。 所以声明数组不要用new Array()的方法。

  • 数组本质上是一个对象。

  • 数组可以用for i和for in循环

  • 伪数组:

    • 伪数组实际是一个对象,他的__proto__指向Object.prototype,没有pop push等方法
    • 它里面的属性都是数字(内容是函数所传进来的参数)和一个length
    • JS里面只有一个伪数组arguments,代表函数里面所传入的所有的参数。
  • forEach

    [1,2,3].forEach(function(value,index){
    			 		   console.log(value)
    			  		   console.log(index)
    	 			})
    复制代码
  • sort&join&concat&map&filter&reduce segmentfault.com/a/119000001…

函数题目

函数声明的五种方式

注意其中一种方式:

  • 函数的name

1具名函数
 function f(x,y){
     return x+y
 }
 f.name // 'f'
 
2匿名函数
 var f
 f = function(x,y){
     return x+y
 }
 f.name // 'f'
 
3具名函数赋值
 var f
 f = function f2(x,y){ return x+y }
 f.name // 'f2'
 console.log(f2) // undefined
 
4window.Function
 var f = new Function('x','y','return x+y')
 f.name // "anonymous"
 
5箭头函数
 var f = (x,y) => {
     return x+y
 }

f.name//"f"
复制代码

只要记住3、4两种特殊的情况就好。

  • this
    • 特例:当call()的第一个参数是undefined的时候, this 是 window.
    • 当启用严格模式的时候,call 里的第一个参数是什么,this 就是什么
  • 函数的调用栈:执行的时候函数进入栈return的时候函数弹出栈
  • Stack Overflow堆栈溢出,超出call stack 函数调用栈。

立即执行函数

作用:创建一个独立的作用域,避免变量污染

(function(){alert('我是匿名函数')} ()) // 用括号把整个表达式包起来
(function(){alert('我是匿名函数')}) () //用括号把函数包起来
//下面的都是执行这个表达式,而不管返回值是什么
!function(){alert('我是匿名函数')}() 
复制代码

另一种创建独立作用域的方法是使用let

异步函数的经典题

下面代码打印出的是6 因为setTimeout是 异步函数,主线程的同步任务执行完毕,异步任务才从任务队列里拿出来执行。 即使时间是0,他也是异步的。也要等到所有代码解析完。等代码解析完毕,i已经是6了。

上面这个同理,当我点击按钮的时候,点击事件才触发(异步),相当于是js早就解析完了代码,i已经变成6了,才执行事件绑定, 事件绑定与触发都是 异步的

闭包经典面试题

经典面试题,循环中使用闭包解决 var 定义函数的问题

for ( var i=1; i<=5; i++) {
	setTimeout( function timer() {
		console.log( i );
	}, i*1000 );
}
首先因为 setTimeout 是个异步函数,所有会先把循环全部执行完毕,这时候 i 就是 6 了,所以会输出一堆 6。

解决办法两种,第一种使用闭包(创建的这个立即执行函数就是一个新块级作用域,并使用了外部的变量i,解析完之后,也不随着i的最终改变而改变)+立即执行函数

for (var i = 1; i <= 5; i++) {
  (function(j) {
    setTimeout(function timer() {
      console.log(j);
    }, j * 1000);
  })(i);
}


第三种就是使用 let 定义 i 了

for ( let i=1; i<=5; i++) {
	setTimeout( function timer() {
		console.log( i );
	}, i*1000 );
}
因为对于 let 来说,他会创建一个块级作用域,相当于刚才的闭包创建的块级作用域。
复制代码

===

=== 而不用==。只需要注意两点

  1. NaN ===NaN//false
  2. 所有即使是内容一样的对象=== 也都是false 。因为地址不一样。

轮播

  • 高级轮播 // 第一步,循环给按钮添加点击事件 //第二步:添加定时器,定时出发,循环轮播,并且进入时暂停轮播 jsbin.com/funumujoqe/…

  • 无缝轮播 说思路就行了,然后说我在工作的时候都不手写录播,主要是为了理解他的原理,然后用别人写好的轮播库swiper,这样工作中的BUG会少一点。

回调函数

callback 是一种特殊的函数,这个函数被作为参数传给另一个函数去调用。这样的函数就是回调函数。 举一个Callback(回调函数)的例子 ,例如:

$button.on('click', function(){})
div.addEventListener('click', function(){})
复制代码

click 后面的 function 是一个回调,因为「我」没有调用过这个函数,是浏览器在用户点击 button 时调用的。

再举一个使用回调函数的例子

function setClock(callBack){
  console.log('1定一个闹钟,三秒钟之后响');
  setTimeout(()=>{
    console.log('2三秒到了,闹钟响了!');
    callBack();
  },3000)
}

function getUp(){
  console.log('3闹钟已经响了,该起床了')
}

setClock(getUp);
复制代码

回调函数一般只和**异步操作(setTimeOut)**在一起才使用!!!

什么是异步

1单线程模式

JavaScript 只在一个线程上运行,JavaScript 同时只能执行一个任务其他任务都必须在后面排队等待

2同步任务(synchronous)和异步任务(asynchronous)

同步任务主线程上排队执行的任务。只有前一个任务执行完毕,才能执行后一个任务。

异步任务不进入主线程、而进入任务队列的任务。只有引擎认为某个异步任务可以执行了(比如 Ajax 操作从服务器得到了结果,或者setTimeOut到时间了(事件循环)),该任务(采用回调函数的形式)才会进入主线程执行

3任务队列和事件循环

JavaScript 运行时,除了一个正在运行的主线程,引擎还提供一个任务队列(task queue),里面是各种需要当前程序处理的异步任务。

首先,主线程会去执行所有的同步任务。等到同步任务全部执行完,就会去看任务队列里面的异步任务。如果满足条件,那么异步任务就重新进入主线程开始执行,这时它就变成同步任务了。等到执行完,下一个异步任务再进入主线程开始执行。一旦任务队列清空,程序就结束执行。

异步任务的写法通常是回调函数。一旦异步任务重新进入主线程,就会执行对应的回调函数。如果一个异步任务没有回调函数,就不会进入任务队列,也就是说,不会重新进入主线程,因为没有用回调函数指定下一步的操作。

JavaScript 引擎怎么知道异步任务有没有结果,能不能进入主线程呢?答案就是引擎在不停地检查,一遍又一遍,只要同步任务执行完了,引擎就会去检查那些挂起来的异步任务,是不是可以进入主线程了。这种循环检查的机制,就叫做事件循环(Event Loop)。

问关于异步、主线程、事件循环(Event Loop)的时候:按照下面回答: 面试官问问题:js是单线程模式的,那么他是怎么实现异步操作的? 答:

  1. js里面的任务分为同步任务异步任务
  2. 同步任务进入主线程一个一个得执行。异步任务进入任务队列,等同步任务执行完了之后,只有触发了某个条件,才把任务队列里面的任务放到主线程执行(比如ajax得到返回的数据,就开始执行回调函数,setTimeOut的时间到了,就执行回调函数)
  3. 异步任务的写法通常是回调函数。一旦异步任务重新进入主线程,就会执行对应的回调函数。
  4. 同步任务执行完之后引擎就一遍一遍得检查。JavaScript 引擎怎么知道异步任务有没有结果,能不能进入主线程呢?答案就是引擎在不停地检查,一遍又一遍,只要同步任务执行完了,引擎就会去检查那些挂起来的异步任务,是不是可以进入主线程了。这种循环检查的机制,就叫做事件循环(Event Loop)。事件循环机制一遍一遍得检查,符合条件,就把异步任务放到主线程里面去执行(比如ajax返回的数据到了,setTimeOut里面的时间到了)

异步操作的方法: 1.回调函数 2.事件监听(触发条件,执行回调函数) 3.ES6:Promise

定时器

它们向任务队列添加定时任务。时间到就添加,然后事件循环就会扫到,扫到了就执行里面的回调函数

Promise

什么是Promise? 举例: $.ajax().then(成功函数,失败函数)

作用:避免回调嵌套,使回调变的可控

ES6划入标准标准。Promise现在是js的内置对象。Promise 对象是 JavaScript 的异步操作的解决方案。Promise 可以让异步操作写起来,就像在写同步操作的流程,(链式使用,then之后可以继续then)而不必一层层地嵌套回调函数

写一个Promise异步操作解决方案

function dosomething(){
    // Promise 接受函数作为参数两个参数,
    // resolve: 异步事件成功时调用
    // reject: 异步事件失败时调用
    return new Promise((resolve, reject) => {
        let result = 异步操作()
	// 下面给出承诺,面对不同的结果,我会 执行 不同的解决方案
	if (result === 'success') 
	    resolve('成功数据')
	else 
	    reject('失败数据')
	})
}

// 异步操作,模拟概率事件
function 异步操作() {
    return Math.random() > 0.5 ? 'success' : 'fail'
}

// 你在dosomething

dosomething()
    // 异步操作成功了,那么我们打印成功的数据
    .then(res => console.log(res))
    //  异步操作失败了,那么我们打印失败的数据
    .catch(res => console.log(res))
复制代码

Sync:同步的意思 Async:异步的意思

MVC与模块化

传统模块化方法:

  1. 使用window
    //module1.js
    !function(){
        var person = window.person = {
            name:"frank",
        }
        //局部变量person和全局变量person用的是同一个地址
    }.call()
    复制代码
    虽然不同变量,但是是同样的地址
    //module2.js
    !function(){
        var person = person;//即var person = window.person;
        console.log(person);
    }.call();
    复制代码
  2. 使用闭包
 //module1.js
!function(){
    var person = {
        name:"mataotao",
        age:18,
    };
    window.mataotaoGrowUp = function(){
        person.age+=1;
        return person.age;
    };
}.call();
复制代码
//module2.js
!function(){
    var newAge = window.mataotaoGrowUp();
    console.log(newAge);//19
}.call();
复制代码

用闭包的好处:

  • 用来 隐藏数据细节 (不告诉你多少岁但是你可以让他涨一岁,隐藏了age 的细节和name)
  • 可以用来 做访问控制 (只能访问到age,无法访问到name)

前端面试题(移动适配,闭包,this,HTTP状态吗,排序思路,页面加载,数组去重)

2 移动端是怎么做适配的?

2016年腾讯前端面试题: 移动端是怎么做适配的? 回答要点:

  1. meta viewport
  2. 媒体查询
  3. 动态 rem 方案

(可以参考我写的博客 CSS5:移动端页面(响应式) CSS9:动态 REM-手机专用的自适应方案) 答:

2.1做手机端页面首先要加上一个meta标签

    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
复制代码

content="width=device-width表示宽度等于设备宽度,意思就是不要将页面宽度变成980px,用设备宽度. user-scalable=no表示用户不以缩放 initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0 初始缩放倍数,最大缩放倍数,最小缩放倍数,都是1.0,即不能缩放

2.2媒体查询

通过媒体查询,根据不同条件,使用不同的css样式。 例如:

    <style>
        @media (max-width: 800px){/*如果媒体满足0到800 之间,那么会应用这里面的样式*/
            body{
                background-color: red;
            }
        }
    </style>
复制代码

2.3动态rem

因为手机需要兼容很多不同宽度的手机设备,所以将长度单位依赖于手机设备宽度,使用动态rem方案,那么就可以在不同手机上实现相同比例的页面缩放而不影响布局。 rem:root em,即<html>font-size. 实现动态rem,主要需要下面两步: 1在<head>标签里加上如下代码,让10rem等于页面宽度

    <head>
      <meta charset="utf-8">
      <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
      <title>动态REM</title>
      <script>
        var pageWidth = window.innerWidth
         document.write('<style>html{font-size:'+pageWidth+'px;}</style>')
      </script>
    </head>
复制代码

2使用sass自动将设计稿的px转换为rem 在scss文件里写这样一个函数:

    @function px( $px ){
    @return $px/$designWidth*10 + rem;
    }

    $designWidth : 640; // 640 是设计稿的宽度,你要根据设计稿的宽度填写。设计师的设计稿宽度需要统一
复制代码

就可以使用px()函数将像素转化为rem。

3 实现圆角矩形和阴影怎么做?

2017年腾讯前端实习面试题(二面): 用过CSS3吗? 实现圆角矩形和阴影怎么做? (搜索MDN border-radius) 答: 用过。例如阴影,圆角,动画,渐变和过渡 1.圆角: 简写属性border-radius。例如 border-radius: 30px; border-radius: 50%; 半径参数可以是长度单位,也可以是百分比单位。

也可以分别设置四个角

border-top-left-radius:     4px 2px;
border-top-right-radius:    3px 4px;
border-bottom-right-radius: 6px 2px;
border-bottom-left-radius:  3px 4px;
复制代码

半径参数可以是一个或两个,一个参数代表圆形圆角,两个参数是椭圆圆角

2.阴影:

语法: box-shadow:inset x-offset y-offset blur-radius spread-radius color 五个参数分别是:投影方式 X轴偏移量 Y轴偏移量 阴影模糊半径 阴影扩展半径 阴影颜色

4 什么是闭包,闭包的用途是什么?

出处同上(一面二面都问了): 什么是闭包,闭包的用途是什么? JavaScript高程P178 闭包的用途

答:

4.1什么是闭包

闭包是指有权访问另一个函数作用域中的变量的函数。 例如

function foo(){
  var local = 1
  function bar(){
    local++
    return local
  }
  return bar
}

var func = foo()
func()
复制代码

bar函数可以访问变量local,bar就是一个闭包。

4.2闭包的用途

  1. 模仿块级作用域
    function A(num) {
        //核心代码
       (funnction(){
        for(var i = 0; i<num; i++) {
          num++;
        }
        })()
        //核心代码结束
        console.log(i)//underfined
      }
    复制代码

匿名自执行函数在内部形成了一个闭包,使i变量只有块级作用域。闭包的本质是函数,其实在这里闭包就是那个匿名函数,这个闭包可以得到函数A内部的活动变量,又能保证自己内部的变量在自执行后直接销毁。

  1. 存储变量 闭包的另一个特点是可以保存外部函数的变量,原理是基于javascript中函数作用域链的特点,内部函数保留了对外部函数的活动变量的引用,所以变量不会被释放
function B(){
   var x = 100;
   return {
       function(){
           return x
       }
   }
 }
var m = B()//运行B函数,生成活动变量 x被m引用
复制代码

运行B函数,生成活动变量 x被m引用, 变量x不会被销毁。 运行B函数,返回值就是B内部的匿名函数,此时m引用了变量x,所以B执行后x不会被释放,利用这一点,我们可以把比较重要或者计算耗费很大的值存在x中,只需要第一次计算赋值后,就可以通过m函数引用x的值,不必重复计算,同时也不容易被修改。 3. 封装私有变量

function Person(){
   var name = 'default';
   this.getName:function(){
       return name;
   }
   this.setName:function(value){
       name = value;
   }
}
console.log(Person.getName())//default
console.log(Person.setName('mike'))
console.log(Person.getName())//mike
复制代码

设置了两个闭包函数来操作Person函数内部的name变量,除了这两个函数,在外部无法再访问到name变量,name也就相当于是私有成员。

5 call、apply、bind 的用法分别是什么?

阮一峰的javascript教程--this 深入浅出 妙用Javascript中apply、call、bind

答:

如果在函数中包含多层的this,this的指向是不确定的。需要把this固定下来,避免出现意想不到的情况。JavaScript提供了call、apply、bind这三个方法,来切换/固定this的指向。

5.1Function.prototype.call()

函数实例的call方法,可以指定函数内部this的指向(即函数执行时所在的作用域),然后在所指定的作用域中,调用该函数。

var obj = {};

var f = function () {
  return this;
};

f() === window // true
f.call(obj) === obj // true
复制代码

call的第一个参数就是this所要指向的那个对象,后面的参数则是函数调用时所需的参数。

5.2Function.prototype.apply()

apply方法的作用与call方法类似,也是改变this指向,然后再调用该函数。唯一的区别就是,它接收一个数组作为函数执行时的参数。

apply方法的第一个参数也是this所要指向的那个对象,如果设为null或undefined,则等同于指定全局对象。第二个参数则是一个数组,该数组的所有成员依次作为参数,传入原函数。原函数的参数,在call方法中必须一个个添加,但是在apply方法中,必须以数组形式添加

function f(x, y){
  console.log(x + y);
}

f.call(null, 1, 1) // 2
f.apply(null, [1, 1]) // 2
复制代码

5.3Function.prototype.bind()

bind方法用于将函数体内的this绑定到某个对象,然后返回一个新函数

bind方法的参数就是所要绑定this的对象。

var counter = {
  count: 0,
  inc: function () {
    this.count++;
  }
};

var func = counter.inc.bind(counter);
func();
counter.count // 1
复制代码

上面代码中,counter.inc方法被赋值给变量func。这时必须用bind方法将inc内部的this,绑定到counter,否则就会出错。

6 HTTP 状态码

出处同上: 请说出至少 8 个 HTTP 状态码,并描述各状态码的意义。

例如:

状态码 200 表示响应成功。

答:

状态码 202 表示:服务器已接受请求,但尚未处理。 状态码 204 表示:请求处理成功,但没有资源可返回。 状态码 206 表示:服务器已经成功处理了部分 GET 请求。

状态码 301 表示:请求的资源已被永久的分配了新的 URI。 状态码 302 表示:请求的资源临时的分配了新的 URI。

状态码 400 表示:请求报文中存在语法错误。 状态码 401 表示:发送的请求需要有通过 HTTP 认证的认证信息。 状态码 403 表示:对请求资源的访问被服务器拒绝了。 状态码 404 表示:服务器上无法找到请求的资源。

状态码 500 表示:服务器端在执行请求时发生了错误。 状态码 503 表示:服务器暂时处于超负债或正在进行停机维护,现在无法处理请求。

7 写出一个 HTTP 请求和响应的内容

出处同上: 请写出一个 HTTP post 请求的内容,包括四部分。 其中 第四部分的内容是 username=ff&password=123 第二部分必须含有 Content-Type 字段 请求的路径为 /path

看我的博客HTTP入门(一):在Bash中curl查看请求与响应

答: 请求:

1 POST /path HTTP/1.1
2 Host: www.baidu.com
2 User-Agent: curl/7.20.0 (x86_64-unknown-linux-gnu) libcurl/7.20.0 zlib/1.2.8
2 Accept: */*
2 Content-Length: 24
2 Content-Type: application/x-www-form-urlencoded
3 
4 username=ff&password=123
复制代码

响应:

1 HTTP/1.1 200 OK
2Cache-Control: private, no-cache, no-store, proxy-revalidate, no-transform
2Content-Length: 2443
2Content-Type: text/html(百度返回的时候百度的数据长度和内容的格式)
2Etag: "5886041d-98b"
2Set-Cookie: BDORZ=27315; max-age=86400; domain=.baidu.com; path=/
3
4<!DOCTYPE html> ...
复制代码

1234567890

8 请说出至少三种排序的思路

这三种排序的时间复杂度分别为

O(n*n) O(n log2 n) O(n + max)

答:

O(n*n) 冒泡排序:遍历整个数组,依次比较相邻两个元素,将小的排在前面,大的排后面,这样一遍循环下来就可以将最大的元素排到最后,除去已经排过的最大的数,然后再次循环以上操作,直到最后一个为止。

O(n log2 n) 快速排序:以第一个元素为基准,比这个元素小的元素排在左边,比这个元素大的排右边,再以该元素左边和右边的第一个元素为基准,在子区间重复以上的操作,直到只有一个数字排序为止。

O(n + max) 基数排序:首先根据个位数的数值,将需要排序的一串数值分配到0-9的桶中。接着将这些桶中的数值重新串起来,形成新的数列。接着根据十位数、百位数直至最高位重复以上操作。

9 页面从输入URL到页面加载显示完成的过程

著名前端面试题:

一个页面从输入 URL 到页面加载显示完成,这个过程中都发生了什么? 这一题是在挖掘你的知识边界,所以你知道多少就要答多少。

可以先查阅一些资料再查,但是不要把自己不懂的东西放在答案里,面试官会追问的。

知乎上:从输入 URL 到页面加载完成的过程中都发生了什么 答:

  1. DNS解析 DNS解析的过程就是浏览器查找域名对应的 IP 地址;
  2. TCP连接 浏览器根据 IP 地址向服务器发起 TCP 连接,与浏览器建立 TCP 三次握手: (1)主机向服务器发送一个建立连接的请求(您好,我想认识您); (2)服务器接到请求后发送同意连接的信号(好的,很高兴认识您); (3)主机接到同意连接的信号后,再次向服务器发送了确认信号(我也很高兴认识您),自此,主机与服务器两者建立了连接。
  3. 发送HTTP请求 浏览器根据 URL 内容生成 HTTP 请求报文。HTTP请求报文是由三部分组成: 请求行, 请求报头和请求正文,其中包含请求文件的位置、请求文件的方式等等。
  4. 服务器处理请求并返回HTTP报文

服务器接到请求后,回想客户端发送HTTP响应报文。HTTP响应报文也是由三部分组成: 状态码, 响应报头和响应报文。服务器会根据 HTTP 请求中的内容来决定如何获取相应的 HTML 文件,并将得到的 HTML 文件发送给浏览器。

  1. 浏览器解析渲染页面 浏览器是一个边解析边渲染的过程。在浏览器还没有完全接收 HTML 文件时便开始渲染、显示网页。在执行 HTML 中代码时,根据需要,浏览器会继续请求图片、CSS、JavsScript等文件,过程同请求 HTML 。

  2. 关闭TCP连接或继续保持连接

(1)主机向服务器发送一个断开连接的请求(不早了,我该走了);

(2)服务器接到请求后发送确认收到请求的信号(知道了);

(3)服务器向主机发送断开通知(我也该走了);

(4)主机接到断开通知后断开连接并反馈一个确认信号(嗯,好的),服务器收到确认信号后断开连接;

10 如何实现数组去重

著名面试题: 如何实现数组去重? 假设有数组 array = [1,5,2,3,4,2,3,1,3,4] 你要写一个函数 unique,使得 unique(array) 的值为 [1,5,2,3,4] 也就是把重复的值都去掉,只保留不重复的值。

要求:

不要做多重循环,只能遍历一次 请给出两种方案,一种能在 ES 5 环境中运行,一种能在 ES 6 环境中运行(提示 ES 6 环境多了一个 Set 对象) 从 JavaScript 数组去重谈性能优化 也谈JavaScript数组去重 答:

ES5: 思路:核心是构建了一个 hash 对象来替代 indexOf. 注意在 JavaScript 里,对象的键值只能是字符串,因此需要 var key = typeof(item) + item 来区分数值 1 和字符串 '1' 等情况。 只循环一遍

function unique(arr) {
  var ret = []
  var hash = {}
  for (var i = 0; i < arr.length; i++) {
    var item = arr[i]
    var key = typeof(item) + item
    if (hash[key] !== 1) {
      ret.push(item)
      hash[key] = 1
    }
  }
  return ret
}
复制代码

ES6:ES2015引入了一种叫作Set的数据类型。顾名思义,Set就是集合的意思,它不允许重复元素出现。 如果重复添加同一个元素的话,Set中只会存在一个。包括NaN也是这样

function unique(array) {
   return Array.from(new Set(array));
}
复制代码

JS题目总结:原型链/new/json/MVC/Promise

1原型链相关

解读: 上图中,Object,Function,Array,Boolean都是构造 函数

第一个框: object是实例对象,他的模板对象(原型对象)在Object()构造函数里面. 构造函数.prototype指向的是原型对象,即模板对象. 由构造函数构造出来的实例对象.__proto__也指向的是原型对象,即模板对象. 所以true.

第二个框: fn是一个实例函数,是由用来构造出函数构造函数造出来的. 所以fn.__proto__ === Function.prototype

任何构造函数.prototype都是一个对象. 因为fn.__proto__ === Function.prototype 所以fn.__proto__.__proto__ === Object.prototype等价于 Function.prototype.__proto__ === Object.prototype 等价于 一个对象.__proto__ === Object.prototype 所以是true

第三个框同理.

第四个框比较难理解: 一个实例函数是由用来构造出函数构造函数造出来的.

Object,Function,Array都是一个实例函数,函数也是一种类型,就像String是一种类型,Number是一种类型一样,函数这个类型里的实例函数由函数的构造函数造出来!很难理解 所以实例函数.__proto__===构造函数.prototype 实例函数的构造函数就是Function

有点鸡生蛋蛋生鸡的感觉.

第五个框同理

2面向对象,new,原型链相关

function fn(){
    console.log(this)
}
new fn()
复制代码

new fn() 会执行 fn,并打印出 this,请问这个 this 有哪些属性?这个 this 的原型有哪些属性? 答: 这个this就是new创建的新对象. this(这个新对象)有__protot__属性,它指向fn构造函数的原型即fn.prototype 这个原型(即fn.prototype)有两个属性:

  1. construct :它的值是构造函数fn
  2. __proto__: 它指向Object.prototype

解读:

  1. fn()是构造函数
  2. new fn()就是一个构造函数new出来的新对象. 他的自有属性为空,共有属性为空,因为都没有设置 因为他的自有属性为空,所以他只有一个__proto__指向构造函数.prototype(即原型)了. 共有属性为空,所以他的原型就是只有constructor指向构造函数和__proto__指向Object.prototype(因为原型本身就是对象类型,所以指向对象的构造函数) 例子:

4 MVC

前端 MVC 是什么?(10分) 请用代码大概说明 MVC 三个对象分别有哪些重要属性和方法。(10分)

答一:

MVC 是什么 MVC 是一种设计模式(或者软件架构),把系统分为三层:Model数据、View视图和Controller控制器。 Model 数据管理,包括数据逻辑、数据请求、数据存储等功能。前端 Model 主要负责 AJAX 请求或者 LocalStorage 存储 View 负责用户界面,前端 View 主要负责 HTML 渲染。 Controller 负责处理 View 的事件,并更新 Model;也负责监听 Model 的变化,并更新 View,Controller 控制其他的所有流程。

答二: MVC就是把代码分为三块

V(view)只负责看得见的东西. M(model)只负责跟数据相关的操作,不会出现DOM,不会出现任何的html/css操作.例如model里只会有初始化数据库,获取数据方法fetch(),保存数据的方法save() C(controller)只负责把这些view和model组合起来,找到view,找到model,使用model完成数据修改业务,并修改view的显示 V:视图 M:数据 C:控制器

MVC是一种代码组织形式,不是任何一种框架,也不是任何一种技术,只是组织代码的思想,要做的就是V和M传给C,C去统筹 在js里,MVC分别由三个对象去担任三个职责

代码一:

window.View = function(xxx){
    return document.querySelector(xxx);
}
复制代码
window.Model = function(object){
    let resourceName = object.resourceName;
    return {
        init: function () { 
        },
        fetch: function () { 
        },
        save: function (object) {
        }
    }
}
复制代码
window.Controller = function(options){
    var init = options.init;
    let object = {
        view:null,
        model:null,
        init:function(view,model){
            this.view = view;
            this.model = model;
            this.model.init();
            init.call(this,view,model);
            this.bindEvents();
        },
        bindevnets:function(){},
    };

    for (let key in options) {
        if(key !=='init'){
            object[key] = options[key]
        }
    };
    return object;
}
复制代码

代码二:

var model = {
    data: null,
    init(){}
    fetch(){}
    save(){}
    update(){}
    delete(){}
}
view = {
    init() {}
    template: '<h1>hi</h1'>
}
controller = {
    view: null,
    model: null,
    init(view, model){
        this.view = view
        this.model = model
        this.bindEvents()
    }
    render(){
        this.view.querySelector('name').innerText = this.model.data.name
    },
    bindEvents(){}
}
复制代码

5 ES5类,原型链,构造函数,new

如何在 ES5 中如何用函数模拟一个类?(10分)

答一:

使用原型对象,构造函数,new来模拟类.

  1. 将公共属性放到原型对象里,并且将构造函数的prototype属性指向原型对象.
  2. 私有属性(自有属性)放到构造函数里去定义.
  3. 将实例化的对象的__proto__指向原型对象. 这样当构造函数创建一个实例化的对象的时候,就即拥有自己的私有变量和方法,也有公有的变量和方法了,实例化出来的对象的私有方法和变量修改都不会互相有影响,只有在修改公有的变量和方法的时候是对所有实例生效的

答二: ES 5 没有 class 关键字,所以只能使用函数来模拟类。

function Human(name){
    this.name = name
}
Human.prototype.run = function(){}

var person = new Human('frank')
复制代码

上面代码就是一个最简单的类,Human 构造函数创建出来的对象自身有 name 属性,其原型上面有一个 run 属性。

数组方法,快排,冒泡,数组去重

数组方法

Array.isArray() Array.isArray(arr) // true

2.实例方法 valueOf(),toString() arr.valueOf() // [1, 2, 3]返回本身 arr.toString() // "1,2,3,4,5,6"

push(),pop() shift()//删除第一个元素

join()//变成一个字符串

concat()//数组合并

reverse()//颠倒数组

slice(起始位置,终止位置)//[起始位置,终止位置)减下来一部分(一小片)数组,一小片,返回一个新数组,原数组不变

splice方法用于删除原数组的一部分成员,并可以在删除的位置添加新的数组成员,返回值是被删除的元素。注意,该方法会改变原数组。(裁剪下来)

var a = ['a', 'b', 'c', 'd', 'e', 'f'];
a.splice(4, 2) // ["e", "f"]
a // ["a", "b", "c", "d"]
复制代码

从下标4开始,往后删2个

sort()//按字典序排序,可以传入函数作为参数规定排序的方式。直接改变了原数组

遍历: map()//映射,并返回一个新数组,里面函数可以调整数组的值

forEach()//forEach方法不返回新数组,只用来遍历数据

filter()//过滤一个数组 [1, 2, 3, 4, 5].filter(function (elem) { return (elem > 3); }) // [4, 5]

indexOf()//indexOf方法返回给定元素在数组中第一次出现的位置. 如果没有出现则返回-1

String的方法

属性:length

方法: var s = new String('abc'); s.charAt(1) // "b" 这个方法完全可以用数组下标替代。 'abc'.charAt(1) // "b" 'abc'[1] // "b"

concat()

slice()减一小块下来,返回新字符串,[起始位置,结束位置) substring()一样,优先使用slice() substr()一样,优先使用slice()

indexOf() 用于确定一个字符串在另一个字符串中第一次出现的位置,返回结果是匹配开始的位置。如果返回-1,就表示不匹配

trim()去除字符串两端的空格,返回新字符串

toLowerCase() toUpperCase() 变换大小写

split()将字符串分割为数组 'a|b|c'.split('|') // ["a", "b", "c"],与jion相对应

Math

Math.floor(),Math.ceil()地板值,天花板值,相对于数轴的。

Math.round()四舍五入

Math.random() [0,1)的随机数

快排

let a = [1,1,1,2,2.2,12,14,23,443,5,5,435,45656768,4,6,7,5,8,33,67,86,2,86,67,845,4,24,5342,1,2]
function quickSort (arr){
  if(arr.length<=1) {return arr}
  //   let midIndex = Math.floor(arr.length/2)
  //   let midItem = arr.splice(0,1)[0]
  let midItem = arr[0]
  let left = []
  let right = [] 
  for(i = 0;i<arr.length;i++){
    if(i!==0){//跳过中间值
      if(arr[i]<midItem){
        left.push(arr[i])
      }else{
        right.push(arr[i])
      }
    }
  }
  return quickSort(left).concat([midItem],quickSort(right))
}
console.log(quickSort(a))
复制代码

jsbin.com/ravujoyopi/…

数组去重

let a=[1,1,1,2,2,2,3,3,3,3,undefined,'xxx','xxx','3','3','undefined']

function f(array){
  let hash = {}
  let returnArr = []
  for(let i=0;i<array.length;i++){
    let arrItemStr = (typeof array[i]) + array[i] + ""//防止undefined和'undefined'转换成字符串然后冲突

    if(hash[arrItemStr]!==1){//说明未进入hash
      hash[arrItemStr]=1
      returnArr.push(array[i])
    }
  }
//   console.log(hash)
  return returnArr
}
console.log(f(a))


//2:
console.log(Array.from(new Set(a)))
复制代码

ES6

了解let const 解构赋值 箭头函数 class promise

转载于:https://juejin.im/post/5c125fbdf265da6166245845

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值