原型:是一个对象,是函数的一个属性prototype(任何函数都有),通过该函数实例化出来的对象都可以继承得到原型上的所有属性和方法。
原型链:访问实例对象的属性时,首先在自己身上查找,如果自身不存在,那么就去它的原型对象(prototype属性)上查找,这个原型对象又有自己的原型,直到找到Object原型(为null),以此形成的类似链条的结构就称为原型链。
function Person(name) {
this.name = name
}
// 修改原型
Person.prototype.getName = function() {}
Person.prototype = {
getName: function() {}
}
注意
:重写原型时,对象的构造函数可能会指向根构造函数Object
Person.prototype = {
getName: function() {}
}
var p = new Person('hello')
p.constructor = Person
console.log(p.__proto__ === Person.prototype) // true
console.log(p.__proto__ === p.constructor.prototype) // true
function Person(name) {
this.name = name
}
var p = new Person('John')
var p1 = new Person('Mike')
console.log(p.__proto__) // Person.prototype
console.log(Person.prototype.__proto__) // Object.prototype
console.log(p.__proto__.__proto__) // Object.prototype
console.log(p.__proto__.constructor.prototype.__proto__) // Object.prototype
console.log(Person.prototype.constructor.prototype.__proto__) // Object.prototype
console.log(p1.__proto__.constructor) // Person
console.log(Person.prototype.constructor) // Person
概念:
闭包的含义是一个函数有权访问另一个函数作用域的函数(A函数中创建B函数,函数B可以以访问到函数A中的变量,函数B就是闭包)。
用途
:在函数外部能够访问到函数内部的变量;函数运行结束以后会保留对这个变量对象的引用,不会回收对象。
不足
:占用更多内存,会引起内存泄漏。
应用场景
:防抖节流–保存上一次运行的setTimeout(不用闭包会重新创建定时器,获取不到上一次定时器时间);函数作为返回值;vue的响应式原理;函数嵌套;
概念
:作用域–>变量或者是函数能作用的范围。
作用域链:当使用一个变量时,首先在当前作用域查找,如果没找到,就去它的上层作用域去查找,直到找到或者到了全局作用域,这样形成的链式查找称为作用域链。
作用域分为函数作用域,全局作用域以及块级作用域。
全局作用域:常常定义在函数外部,全局作用域变量可以在任意位置访问。
局部作用域(函数作域):定义在函数内部,只能在函数中使用的变量,作用范围是从函数开始到结尾。
块级作用域:ES6 提供 let & const 变量实现块级作用域。
块级作用域应用场景
:内部变量会覆盖外部变量(用来计数的循环变量泄漏为全局变量)。
作用
:作用域链的作用是保证对执行环境有权访问的所有变量的有序访问,通过作用域链,可以访问到外层环境的变量和函数。
执行上下文分为三种:
全局执行上下文:只有一个,程序首次运行时创建,它会在浏览器中创建一个全局对象(window对象),使this指向这个全局对象。
函数执行上下文:函数被调用时创建,每次调用都会为该函数创建一个新的执行上下文。
Eval 函数执行上下文。
执行上下文
:在执行一点JS代码之前,需要先解析代码。解析的时候会先创建一个全局执行上下文环境
,先把代码中即将执行的变量、函数声明都拿出来,变量先赋值为undefined,函数先声明好可使用。这一步执行完了,才开始正式的执行程序。执行上下文栈
:JavaScript引擎使用执行上下文栈来管理执行上下文。概念
:this 是执行上下文中的一个属性,它指向最后一次调用这个方法的对象。
指向
函数调用:指向全局对象window
方法调用:指向这个方法的对象
构造器调用:如果一个函数用 new 调用时,函数执行前会新创建一个对象,this 指向这个新创建的对象。
apply 、 call 和 bind 调用:这三个方法都可以显示的指定调用函数的 this 指向。其中 apply 方法接收两个参数:一个是 this 绑定的对象,一个是参数数组。call 方法接收的参数,第一个是 this 绑定的对象,后面的其余参数是传入函数执行的参数。bind 方法通过传入一个对象,返回一个 this 绑定了传入对象的新函数。
都是改变函数this指向的方法
同步是指一个任务完成后才能执行另一个任务。
异步是把一个任务分成两段,先执行第一段,然后转而执行其他任务,等做好了准备,再回过头执行第二段。排在异步任务后面的代码,不用等待异步任务结束会马上运行。
异步编程的方法(JS 异步编程进化史:callback -> promise -> generator -> async + await)
:
console.log('script start') //1. 打印 script start
setTimeout(function(){
console.log('settimeout') // 4. 打印 settimeout
}) // 2. 调用 setTimeout 函数,并定义其完成后执行的回调函数
console.log('script end') //3. 打印 script start
// 输出顺序:script start->script end->settimeout
console.log('script start')
let promise1 = new Promise(function (resolve) {
console.log('promise1')
resolve()
console.log('promise1 end')
}).then(function () {
console.log('promise2')
})
setTimeout(function(){
console.log('settimeout')
})
console.log('script end')
// 输出顺序: script start->promise1->promise1 end->script end->promise2->settimeout
async function async1(){
console.log('async1 start');
await async2();
console.log('async1 end')
}
async function async2(){
console.log('async2')
}
console.log('script start');
async1();
console.log('script end')
// 输出顺序:script start->async1 start->async2->script end->async1 end
Promise是异步编程的一种解决方案,它是一个对象,可以获取异步操作的消息,他的出现大大改善了异步编程的困境,避免了地狱回调。
Promise的实例有三个状态:Pending(进行中),Resolved,Rejected
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
Promise.resolve(11).then(function(value){
console.log(value); // 打印出11
});
方法
使用ajax发一个A请求后,成功后拿到数据,需要把数据传给B请求;那么需要如下编写代码:
let fs = require('fs')
fs.readFile('./a.txt','utf8',function(err,data){
fs.readFile(data,'utf8',function(err,data){
fs.readFile(data,'utf8',function(err,data){
console.log(data)
})
})
})
使用场景
遇到发送多个请求并根据请求顺序获取和使用数据的场景,就可以使用Promise.all来解决
当要做一件事,超过多长时间就不做了,可以用这个race方法来解决
await 等待的是一个表达式,这个表达式的计算结果是 Promise 对象或者其它值。
await 表达式的运算结果取决于它等的是什么。
单一的 Promise 链并不能发现 async/await 的优势,但是,如果需要处理由多个 Promise 组成的 then 链的时候,优势就能体现出来了(很有意思,Promise 通过 then 链来解决多层回调的问题,现在又用 async/await 来进一步优化它)。
//创建 Object 对象
var p = new Object()
//动态添加属性和方法
p.name = 'Tom'
p.age = 18
p.setName = function(name){
this.name = name
}
var p = {
name = 'Tom'
age = 18
setName: function(name){
this.name = name
}
}
function Person(name, age){
this.name = name
this.age = age
this.setName = function(name){
this.name = name
}
}
var p1 = new Person('Tom',18)
function Person(name, age){
this.name = name
this.age = age
}
Person.prototype.setName = function(name){
this.name = name
}
var p1 = new Person('Tom',18)
一个对象从另一个对象中继承属性,达到的效果是在一个对象中可以使用另一个对象中定义的属性。
Dog.prototype = new Animal('Tommy')
function Employee(name, position) {
Person.call(this, name) // 在子类型的构造函数内部调用父类型的构造函数,传入name参数
this.position = position
}
// 子类型
function Dog(name, age) {
Animal.call(this, name) // 借用构造函数继承属性
this.age = age
}
// 将子类型的原型设置为父类型的实例
Dog.prototype = new Animal()
组合继承是将原型链继承和借用构造函数继承相结合的一种继承方式,通过这种方式可以解决原型链继承和借用构造函数继承各自的缺点。
组合继承通过借用构造函数来继承属性,通过将子类型的原型设置为父类型的实例来继承方法。
// 基于原型对象创建新对象
var anotherPerson = Object.create(person)
function inheritPrototype(subType, superType) {
var prototype = Object.create(superType.prototype) // 创建对象
prototype.constructor = subType // 增强对象
subType.prototype = prototype // 赋值对象
}
// 子类型
function Dog(name, age) {
Animal.call(this, name) // 继承属性
this.age = age
}
// 使用寄生式继承来继承父类型的原型
inheritPrototype(Dog, Animal)
JavaScript代码运行时,需要分配内存空间来储存变量和值。当变量不在参与运行时,就需要系统收回被占用的内存空间,这就是垃圾回收。
垃圾回收方式