前端开发知识点以及相关面试题(一)

问题: 说出下面代码的执行结果,并分析原因。

function foo(a) {
    a = a * 10
};

function bar(b) {
    b.value = 'new'
};

var a = 1;

var b = { value: 'old' };

foo(a);
bar(b);

console.log(a);
console.log(b);

答:通过代码执行,我们发现:

  • a的值没有发生改变
  • b的值发生了改变

分析原因以及知识点:这是因为Number类型的a是按值传递的,而Object的b是按共享传递的。

js中这样设计的原因是:按值传递的不会占很多内存,并且按值传递保证了其访问速度;按共享传递的类型,是复制其引用,而不是复制其整个值,也就是说只复制了一个指针,并没有复制它整个对象。

问题: 如何理解javaScript的原型

答: 可以从以下几点来答,以下几点记住并且要理解:

  • 所有的引用类型(数组,对象,函数)都具有对象的特性,也就是说可以自由扩展属性(Null除外);
  • 所有的引用类型(数组,对象,函数)都有一个__proto__属性,属性值是一个普通的对象;
  • 所有的函数都有一个property属性,属性值也是一个普通的对象;
  • 所有的引用类型(数组,对象,函数)__proto__属性的属性值指向的是它的构造函数的property属性值
function Foo(name, age) {
    this.name = name
    this.age = age
}

Foo.prototype.alertName = function() {
    alert(this.name)
}

var f = new Foo('郭朝夕', 25)
f.printName = function() {
    console.log(this.name)
}
f.printName()
f.alertName()

f.printName()很好理解。可是f.alertName发生了什么呢?这里有一句话需要记住:当试图得到一个对象的某个属性时,如果对象自身没有这个属性,那么就会去这个对象的__proto__(也就是它的构造函数的prototype)中去寻找,所以f.alertName = Foo.prototype.alertName 也就不难理解了。

那么如何判断一个属性是不是对象本身的呢?这里用hasOwnProperty方法。

原型链的理解:当你试图得到一个对象的某个属性时,如果对象本身没有这个属性,会去它的__proto__中去寻找,如果还是没有找到就继续去它的__proto__.__proto__中去寻找,这样一直向上寻找,你会发现这是一个链式结构,所以我们称作“原型链”。如果一直找到最上层都没找到,就会返回undefined,最上层是Object.prototype.__proto__ === Null

作用域和闭包

var list = document.getElementByTagName('li');
for (var i = 0; i < list.length; i++) {
    list[i].addEventListener('click', function(j) {
        return function(j) {
            alert(j + 1)
        }
    }(i))
}

全局上下文

在一段js代码执行之前,会先解析代码,解析的时候会先创建一个全局执行上下文环境,会把即将执行的变量、函数都显拿出来。变量暂时为undefined,函数则先声明好可使用。这一步做完了,然后才开始执行代码。

函数上下文

一个函数在执行之前,也会创建一个函数执行上下文,和全局执行上下文差不多,只不过多了 this,arguments和参数。

总结:

  • 范围: 一段script、js代码或者函数
  • 全局上下文: 变量定义,函数声明
  • 函数上下文: 变量定义,函数声明

this 

this的值在执行的时候才能确认,而不是定义的时候!

作用域

在es6之前,js中没有块级作用域,只有全局作用域和函数作用域。

作用域链

自由变量在父级作用域中寻找,如果找不到继续向父级寻找,直找到全局作用域还是找不到为止。这种一层一层的关系,就是作用域链

var a = 100;
function F1() {
    var b = 50
    function f2() {
        var c = 30
        console.log(a)    // 自由变量 顺作用域链寻找
        console.log(b)    // 自由变量 顺作用域链寻找
        console.log(c)  // 本作用域的变量
    }
    f2()
}

F1()

闭包

闭包主要由两个应用场景:

  1. 函数作为返回值
  2. 函数作为参数传递

函数作为返回值的示例

function F1() {
    var a = 100
    return function() {
        console.log(a)
    }
}

var f1 = F1()
var a = 200
f1()

如以上代码示例,自由变量将从作用域链中寻找,依据的是函数定义时的作用域,而不是函数执行时的作用域。

函数作为参数传递的示例

function F1() {
    var a = 100
    return function() {
        console.log(a)
    }
}

function F2(f1) {
    var a = 200
    console.log(f1())
}

var f1 = F1()
F2(f1)

异步

console.log(100)
setTimeout(function(){
    console.log(200)
}, 1000)
console.log(300)

如果看上面这个例子很容易让人以为会先打印100,过一秒钟以后打印200,最后打印300.但实际上运行起来不是这个样子的,会打印100,紧接着打印300,1秒以后打印300. 这就是异步,异步不会阻塞程序的运行。

异步的应用场景主要由两方面:

  1. setTimeout,setInterval定时器
  2. 网络请求 Ajax,img加载

es6/7 新标准的考查

箭头函数

箭头函数的出现一是让代码更加简洁,二是解决了Es6之前函数中this是全局变量的情况

module

如果只是输出一个对象,使用export default即可。

//在utils.js中定义好
export default {
    a: 100
}

在其他文件中引用
import obj from './utils.js'

如果要输出多个对象和方法,就不能使用export default了,且在引用的时候要用{...}

export function fn1() {
    alert('fn1')
}

export function fn2() {
    alert('fn2')
}

import {fn1, fn2} from './utils.js'

fn1()
fn2()

class

es6的class就是取代之前构造函数初始化对象的形式,使语法看起来更加接近面向对象的写法,如下所示

function MathHandle(x, y) {
    this.x = x
    this.y = y
}
MathHandle.prototype.add = function() {
    return this.x + this. y
}

var m = new MathHandle(1, 2)
console.log(m.add())
class MathHandle {
    constructor(x, y) {
        this.x = x
        this.y = y
    }
    add() {
        return this.x + this.y
    }
}

var m = new MathHandle(1, 2)
console.log(m.add())

es6 class 实现继承

class Animal {

    constructor(name) {
        this.name = name
    }

    eat() {
        console.log(`${this.name} eat`)
    }

}

class Dog extends Animal {

    constructor(name) {
        super(name)
        this.name = name
    }    

    say() {
        console.log(`${this.name} say`)
    }

}

const dog = new Dog('萨摩耶')
dog.eat()
dog.say()

set和map

set类似于数组,可是数组允许元素重复,而set不允许数组重复

const s = new Set()

s.add(1).add(2).add(2)

console.log(s.size)    // 2
console.log(s.has(1))    // true
console.log(s.has(2))    // true
console.log(s.has(3))  // false
s.delete(2)
console.log(s.has(2))  // false
s.clear()
console.log(s)  // Set(0) {}

 

const set = new Set(['姓名', '性别', '年龄'])

for (let item of set.keys()) {
    console.log(item)
}
// '姓名' 
// '性别' 
// '年龄'

for (let item of set.values()) {
    console.log(item)
}
// '姓名' 
// '性别' 
// '年龄'

for (let item of set.entries()) {
    console.log(item)
}
// ['姓名', '姓名']
// ['性别', '性别']
// ['年龄', '年龄']

set.forEach((value, key) => {
    console.log(key + ':' + value)
})

// [姓名:姓名]
// [性别: 性别]
// [年龄: 年龄]

map类似于对象,可是普通对象的key必须是字符串或者数字,而map的key可以是任何数据类型

先来看看map可以用非字符串或者数字做key的特性

const map = new Map()
let obj = {p: 'hello world'}
map.set(obj, '郭朝夕')
map.get(obj)  // 郭朝夕

map.has(obj) // true

map.delete(obj)  // true
map.has(obj)   //false

map实例的属性和方法如下:

size: 获取成员的数量

set: 设置成员的key和value

get: 获取成员的属性值

has: 判断成员是否存在

delete: 删除成员

clear: 清空所有成员

const map = new Map()
map.set('姓名', '郭朝夕')
map.set('性别', '男')
console.log(map.size)  // 2
console.log(map.has('姓名'))  // true
console.log(map.get('姓名')) // 郭朝夕
map.delete('姓名')
console.log(map.has('姓名'))  // false
map.clear()
console.log(map)

map实例的遍历方法有:

keys: 返回键名的遍历器

values:  返回键值的遍历器

entries: 返回所有成员的遍历器

forEach: 遍历map的所有成员

promise

三个状态 两个过程 一个方法  3-2-1

三个状态: pending、fulfilled、rejected

两个过程: Pending→fulfilled   fulfilled→rejected

一个方法: then

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值