js
1. 同步与异步
js是单线程,在执行本轮任务的时候,会出现同步和异步。 代码从上到下依次执行,同步代码通过一个个入栈出栈去执行,遇到异步代码时,先发起异步调用,异步中的回调函数会放到任务队列中去等待,等待本轮同步任务全部结束清空栈之后,再去消息队列里去执行异步任务。 异步回调函数中的结果是需要拿到主线程中去运行展示的。 回来的内容结果,在主线程如何接 在发起异步调用的时候,将当前代码里的一个函数传进去,在异步执行回调的时候再去执行外面传入的函数,将异步调用的结果当作实参传到外面传入的这个函数里。
回调函数就是为了解决:异步执行完的结果如何回来让我们获取并使用的
举例:想要给桌子刷油漆,找一个人去买油漆,这个油漆最后怎么回到我手里,就是我给你一个桶,你把油漆装到这个桶里,然后再给我,我拿这个桶就可以了。
函数的调用时需要回到函数声明的地方去执行的, 想要使用异步调用时返回的结果,传入一个函数,将返回结果作为传入函数的实参 通过回调函数的方式,能够让异步的结果回来 两个线程之间传值的问题,就比如我需要在请求到一个接口数据后,再去执行下一个任务,需要依赖前一个的结果的时候,再去发送下一个请求。就需要下一次的请求写在你的回调里面。多个请求形成回调地狱 调试难, 不好看
2. Promise async/await generator
为了增高代码的可阅读性 出现了promise,现在的所有的异步处理都是基于promise的,可能写法不太一样 无论异步成功失败都会告诉你 异步操作之后会有三种状态
-
我们一起来看一下这三种状态:
pending:等待状态,比如正在进行网络请求,或者定时器没有到时间。fulfill:满足状态,当我们主动回调了resolve时,就处于该状态,并且会回调.then()reject:拒绝状态,当我们主动回调了reject时,就处于该状态,并且会回调.catch()
如果多个任务的话,使用promise,会频繁的.then,链式调用过多
此时就出现了async和await
async/await 是个语法糖,是generator的语法糖
给函数声明的时候,在函数的后面加个 * ,这个函数一旦调用,返回的叫生成器对象,里面的代码通通不执行。什么时候执行呢?这个生成器的对象有一个方法叫next(),当去调用next()的时候才会去执行,每调用一次代码只会执行到yields后面的代码;下一次再调用next()才会继续执行后面的代码 。
yield 代表停

如果有五次
什么是栈和堆
栈先进后出 堆通过优先级分配
js内置对象
什么是原型
在js中是使用一个构造函数来新建一个对象的,每一个构造函数内部都有一个prototype属性,这个属性是个对象,这个对象包含了可以由构造函数的所有实例的共享的属性和方法。使用构造函数新建一个对象后,这个对象的内部包含将包含一个指针,这个指针指向构造函数prototype属性对应的值,在es5中,这个指针被称为对象的原型。
一般来说,我们是不应该能获取到这个值的,现在浏览器中都实现了--proto--属性来让我们访问这个属性。最好不要使用这个属性,es5中新增了object.getPrototypeOf方法来获取到对象的原型。
当访问一个对象的属性时,如果内部不存在这个属性,会去原型对象里去找这个属性,这个原型对象又会有自己的原型,于是就这样一直找下去,这就是原型链的概念,原型链的尽头是object.prototype,所以这就是新建的对象为什么可以使用toString的原因。它们有什么特点,js对象是通过引用来传递的,我们创建的每一个新对象实体中并没有自己的原型副本,当我们修改原型时,与之相关的对象也会继承这一改变。
js作用域链
内部的函数使用变量,会在当前作用域下,没有的话会到上一层找,一直找到全局环境,如果没找到会报错
箭头函数和普通函数
function fun() {
return 100;
}
const fun = function() {
return 100;
}
const fun = () => {
return 100
}
// 箭头函数 () 定义参数,如果只有一个参数,可以不写括号
// 箭头函数如果返回值只有一行代码,可以不写return,直接写返回值
const fun = () => 100
console.log(fun()) // 100 这里调用上面四个函数是没有区别的
// 区别:this指向不同,普通函数谁调用this指向谁;
// 箭头函数在哪里定义函数,this指向谁,指向外部作用域
// 普通函数
let obj = {
name: '小明',
age: '12',
sayName() {
let self = this
//2.// console.log(this) // obj对象
//1.// console.log(`我是${this.name}`)
setTimeout(funtion(){
//2.//console.log(this) // window
console.log(self) // obj对象
})
}
}
//1.// obj.sayName() // 我是小明
obj.sayName()
// 箭头函数
let obj = {
name: '小明',
age: '12',
sayName() {
setTimeout(funtion(){
// console.log(this) // obj对象
console.log(this.name)
})
}
}
obj.sayName()
this对象的理解
this是执行上下文中的一个属性,它指向的是最后一次调用这个方法的对象。 在开发中,this的指向可以通过四种模式来判断 函数调用,当一个函数不是一个对象的属性时,直接作为函数来调用时,this指向全局对象; 调用模式,如果一个函数作为一个对象的方法来调用时,this指向这个对象; 构造器调用模式,如果一个函数用new调用时,函数执行前,会先创建一个对象,this指向新创建的这个对象; call、apply、bind调用时, 这三个方法都可以显式的指定调用函数的this指向, 其中apply接收两个参数,一个是this绑定的对象,一个是参数数组。 call接收的参数,一个是this绑定的对象,后面其余的参数是函数执行的参数,在使用call方法时,传递给函数的参数必须逐个列举出来。 bind方法传入一个对象,返回了一个this绑定了传入对象的新函数,这个函数的this除了使用new时会被改变,其他情况下都不会被改变
this关键字
// 1. 直接输出this指向全局对象
console.log(this) // window
// 2. 全局函数其实是window(全局对象)的方法
function fun () {
console.log(this)
}
fun () // window
// 3. this放在方法中,this指向调用这个方法的对象
let cat = {
name: '喵喵',
sayName() {
console.log("我是" + this.name)
}
}
cat.sayName() // 我是喵喵
// 4. dom事件中的对象this指向这个dom对象
<button>按钮</button>
const btn = document.querySelector("button")
btn.onclick = function () {
console.log (this) // button
}
// 5. 构造函数中的this指向的是new创建的这个对象
function F() {
this.name = "小明"
}
let f = new F()
console.log(f) // 会输出整个大的F对象
// 6. 箭头函数中没有this
// 普通函数,谁调用指向谁
// 箭头函数外指向谁就指向谁
let cat = {
name: '喵喵',
sayName() {
// console.log(this) // {name : "喵喵", sayName: f}
setTimeout(function() {
console.log(this) // window
}, 1000)
}
}
let cat = {
name: '喵喵',
sayName() {
// setTimeout是window的一个方法,如果改成箭头函数就可以指向外面的this
setTimeout(() => {
console.log(this) // {name : "喵喵", sayName: f}
}, 1000)
}
}
new做了什么
// 构造函数是用来创建对象的
// new 关键字会创建一个对象;将构造函数中的this指向创建出来的对象
function F() {
this.name = "小明"
}
let f = new F()
console.log(f) // 会输出整个大的F对象
call apply bind
// call是一个方法,是函数的方法
function fun () {
// console.log('this', this) // window
console.log(this.name) // 喵喵
}
let cat = {
name: "喵喵"
}
// call可以调用函数
// call可以改变函数中this的指向
// fun.call() // hello
// 将函数中的this指向cat
fun.call(cat)
let dog = {
name: "旺财",
sayName() {
console.log('我是' + this.name)
},
eat(food) {
console.log("我喜欢吃" + food)
}
eats(food1, food2) {
console.log("我喜欢吃" + food1+food2)
}
}
let cat = {
name: "喵喵"
}
// dog.sayName() // 我是旺财
// 让猫去用狗的sayName
// dog.sayName.call(cat) // 我是喵喵
// call第一个参数是要改变this的对象,第二个参数是要给eat传的参数,参数可以依次往后传
dog.eat.call(cat, "鱼") // 我喜欢吃鱼
dog.eats.call(cat, "鱼", "肉") // 我喜欢吃鱼肉
// apply的区别是参数二是数组
dog.eat.apply(cat, ["鱼", "肉"]) // 我喜欢吃鱼肉
// bind 传参方式和call一样
// bind不会调用这个函数,会返回一个函数
dog.eat.bind(cat, "鱼", "肉") // 什么都没输出
let fun = dog.eat.bind(cat, "鱼", "肉")
fun() // 我喜欢吃鱼肉
基于call继承
// 继承:子类可以使用父类的方法
// call实现多重继承
function Animal () {
// 2. this也就指向小cat了
this.eat = function () {
console.log("吃东西")
}
}
function Bird () {
// 2. this也就指向小cat了
this.fly = function () {
console.log("飞翔")
}
}
function Cat() {
// 1. this指向小cat
Animal.call(this)
Bird.call(this)
}
let cat = new Cat()
// 3. 小cat就可以调用eat
cat.eat() // 吃东西
cat.fly() // 飞翔
什么是闭包,为什么用它
闭包是指有权访问另一个函数作用域中变量的函数 创建闭包最常见的方式,在一个函数内,创建另一个函数,创建的函数可以访问当前函数的局部变量 闭包两个常用的用途 一是在函数外部能访问到函数内部的变量,通过使用闭包可以通过外部调用闭包函数,从而在外部访问到函数内部的变量,可以使用这种方法创建私有变量 二是使已经运行结束的函数上下文中的变量对象继续留在内存中,因为闭包保留了这个对象的引用,所以这个变量对象不会被回收,
闭包的本质就是作用域链的一个特殊的引用,只要了解了作用域链的创建过程就能了解闭包的实现原理
// 闭包:函数嵌套函数,内部函数就是闭包
// 正常情况下,函数执行完之后,里面的内部变量会被销毁(释放内存空间)
function outerFun () {
let a = 10
function innerFun () {
// a是可以访问外部变量的
console.log(a)
}
return innerFun
}
let fun = outerFun()
// 闭包,内部函数innerFun没有执行完成,外部函数outerFun变量a不会被销毁
fun() // 10 变量不会被销毁
闭包应用:封装代码实现模块化
定义了两个全局变量,两个全局函数
使用闭包封装起来
ajax是什么,如何创建ajax
ajax就是通过js的异步通信,从服务器获取xml文档,从中提取数据,更新当前页面的对应部分,而不用刷新页面。 ajax分为以下几个步骤:
//1.获取所有要操作的元素
var btn = document.getELementById('btn');
var tb = document.getELementById('tb');
//2. 通过按钮的点击事件,发送请求内容
btn.onclick = function () {
//2.1 使用ajax进行异步请求
var xhr = new XMLHttpRequest();
xhr.open('GET','/ajax/day2/table.php');
xhr.send(null);
xhr.onreadystatechange= function () {
//2.2 进行条件判断,何时可以接收响应体内容
if (xhr.readyState === 4 && xhr.status === 200){
//2.3 使用JSON.parse()方法将得到的响应体内容转换为js中可使用的对象形式
var datas = JSON.parse(xhr.responseText);
//2.4 根据响应体内容进行结构的动态创建
// 遍历得到的二维数组结构
for (var i = 0; i < datas.length; i++){
var tr = document.createElement('tr');
tb.appendChild('tr');
for (var j = 0; j < datas[i].length; j++){
var td = document.createElement('td');
td.innerText = datas[i][j];
tr.appendChild(td);
}
}
}
}
};
什么是原型
每一个对象都有它的一个原型对象,它可以使用自己原型对象上的属性和方法
let cat ={
name: '喵喵'
}
cat.__proto__.eat = function () {
console.log("吃鱼")
}
cat.eat()
获取原型的方式
ES5:
-
通过对象的__proto__获取 原型对象,使用上面的方法
-
通过构造函数的prototype属性获取 原型对象 ES6:
-
通过类的prototype属性 获取原型
// ES5用构造函数创建对象
function Cat (name, age) {
this.name = name
this.age = age
}
// 通过构造函数创建对象
let cat = new Cat("喵喵", "2")
Cat.prototype.eat = function () {
console.log('吃鱼')
}
cat.eat()
原型对象的用处
let date = new Date()
console.log()
// 通过原型扩展date对象,
// 构造函数的prototype可以获取原型,给原型加一个方法
Date.prototype.formate = function () {
let year = this.getFullYear()
let month = this.getMonth() + 1
let date = this.getDate()
return `${year}年${month}月${date}日`
}
// 目标:输出**年**月**日
console.log(date.formate())
类 ES6的语法
// es6可以用类创建对象,类可以加属性和方法
clas Cat {
constructor(name, age) {
this.name = name
this.age = age
}
}
Cat.prototype.eat = function () {
console.log('吃鱼')
}
let cat = new Cat("喵喵", 2)
cat.eat()
什么是继承,ES6通过类继承
管理员有登录、增删改用户 普通人有登录
class User {
constructor (username, password) {
this.username = username
this.password = password
}
login () {
console.log('登录')
}
}
// Admin 通过extends继承 User类,可以使用里面的属性和login方法
// 自己又新增了删除方法
class Admin extends User {
deletePerson () {
console.log('删除一个人')
}
}
let admin = new Admin()
admin.login()
admin.deletePerson()
继承 ES5通过构造函数基于原型继承
两个构造函数如何建立联系 Admin可以使用User上的方法, Admin可以使用自己原型对象上的方法 让Admin的原型设置为User的实例方法,User实例有User构造函数的属性和方法
function User (username, password) {
this.username = username
this.password = password
this.login = function () {
console.log('登录')
}
}
function Admin () {
this.deletePerson = function () {
console.log('删除一个人')
}
}
Admin.prototype = new User()
let admin = new Admin()
admin.login()
什么是原型链
一个对象有原型对象,它的原型对象还有原型对象,一直往上最终找到Object找到原型Object.prototype,所有对象的原型都是Object.prototype
function User (username, password) {
this.username = username
this.password = password
// this.login = function () {
// console.log('登录')
// }
}
function Admin () {
this.deletePerson = function () {
console.log('删除一个人')
}
}
// 如果在Object.prototype加了方法,因为所有对象的最上层都是Object.prototype,那么所有的引用类型都可以调用这个方法,数组,对象,正则表达式,日期对象
Object.prototype.login = function () {
console.log('Object原型上的登录方法')
}
Admin.prototype = new User()
let admin = new Admin()
admin.login() // Object原型上的登录方法
let arr = [1, 2, 3]
arr.login() // Object原型上的登录方法