问题: 说出下面代码的执行结果,并分析原因。
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()
闭包
闭包主要由两个应用场景:
- 函数作为返回值
- 函数作为参数传递
函数作为返回值的示例
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. 这就是异步,异步不会阻塞程序的运行。
异步的应用场景主要由两方面:
- setTimeout,setInterval定时器
- 网络请求 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