// "use strict"
// 1.普通的函数被独立的调用
function foo() {
console.log('foo', this);
}
foo()//window
// 2.函数定义在对象中,但是独立调用
var obj = {
name: "why",
bar: function() {
console.log("bar", this)
}
}
var baz = obj.bar
baz()//window
// 3.高阶函数
function test(fn) {
fn()
}
test(obj.bar)//window
// 4.严格模式下,独立调用的函数中的this指向的是undefind
// "use strict"
function foo() {
console.log("foo函数", this);
}
var obj = {
name: "why",
bar: foo
}
obj.bar()//obj
function Foo() {
console.log("foo函数", this);
this.name = "why"
}
new Foo()//Foo {}
function foo(name, age, height) {
console.log("foo函数", this);
console.log(name, age, height)
}
// ()调用
foo("why", 18, 1.88)
// apply
// 第一个参数绑定this
// 第二个参数:传入额外的实参,以数组的形式
foo.apply("apply", ["why", 30, 1.98])
// call
// 第一个参数:绑定this
// 参数列表:后续的参数以参数列表的形式传入实参
foo.call("call", "james", 25, 1.80)
function foo(name, age, height, address) {
console.log("foo函数", this);
console.log(name, age, height, address)
}
var obj = {
name: "why"
}
// 需求:调用foo时,总是绑定到obj对象上(不作为obj对象方法的方式)
// 1.bind函数的基本使用
var bar = foo.bind(obj)
bar()//obj
bar()//obj
bar()//obj
// 2.bind函数的其他参数
var bar2 = foo.bind(obj, "why", 18, 1.99)
bar2("james")//why, 18,1.99, james
优先级:
情况一:如果在显示绑定中,我们传入一个null或者undefind,那么这个显示绑定会被忽略,使用默认规则:window
情况二:创建一个函数的间接引用,这种情况使用默认绑定规则
(obj2.foo = obj1.foo)()
情况三:箭头函数
// 1.之前的方式
function foo1() {}
var foo2 = function(){}
// 2.箭头函数
var foo3 = (name, age) => {
console.log(name, age);
}
nums.forEach(item => {})
nums.forEach(item => console.log(item))
nums.filter(item => true)
var foo = () => {
return { name: "abc" }
}
//返回对象时,如果你想省略大括号,需要在对象外加小括号
var bar = () => ({name: "abc"})
// 1.箭头函数中没有this
// 所以当我们在函数中使用this时,它会在上层作用域中查找this
var bar = () => {
console.log('bar:', this);
}
bar()//window
bar.apply('aaa')//window
// 2.this的查找规则
var obj = {
name: "obj",
foo: function() {
var bar = () => {
console.log("bar", this)
}
return bar
}
}
var fn = obj.foo()
fn.apply("bbb") //obj
var obj2 = {
name: "obj",
foo: () => {
var bar = () => {
console.log("bar", this)
}
return bar
}
}
var fn2 = obj2.foo()
fn2.apply("bbb") //window
// 网络请求场景
function request(url,fn) {
var results = ["a", "b", "c"]
fn(results)
}
var obj = {
names: [],
network: function() {
request("/lists", res => this.names = [].concat(res))
}
}
obj.network()
console.log(obj.names)
var name = "window"
var person = {
name: "person",
sayName: function () {
console.log(this.name);
}
}
function sayName() {
var sss = person.sayName;
sss();//window
person.sayName();//person
(person.sayName)();//person :这个跟上面那个一样是隐式绑定
(b = person.sayName)()//winow:间接函数引用,返回一个独立的函数,然后独立的函数调用
}
sayName()
// 面试题2
var name = "window"
var person1 = {
name: "person1",
foo1: function () {
console.log(this.name);
},
foo2: () => console.log(this.name),
foo3: function () {
return function () {
console.log(this.name)
}
},
foo4: function() {
return () => {
console.log(this.name)
}
}
}
var person2 = { name: "person2"}
person1.foo1();//person1
person1.foo1.call(person2);//person2
person1.foo2();//window
person1.foo2.call(person2); //window
person1.foo3()();//window
person1.foo3.call(person2)();//window
person1.foo3().call(person2);//person2
person1.foo4()(); //person1
person1.foo4.call(person2)();//person2
person1.foo4().call(person2);//person1
// 面试题3
var name = "window"
function Person(name) {
this.name = name
this.foo1 = function () {
console.log(this.name);
},
this.foo2 = () => console.log(this.name),
this.foo3 = function () {
return function () {
console.log(this.name)
}
},
this.foo4 = function () {
return () => {
console.log(this.name)
}
}
}
var person1 = new Person("person1")
var person2 = new Person('person2')
person1.foo1();//person1
person1.foo1.call(person2);//person2
person1.foo2();//window是错的,构造函数有自己的作用域,箭头函数会在这个作用域中找到person1
person1.foo2.call(person2); //person1:理由同上
person1.foo3()();//window
person1.foo3.call(person2)();//window
person1.foo3().call(person2);//person2
person1.foo4()(); //person1
person1.foo4.call(person2)();//person2
person1.foo4().call(person2);//person1
// 面试题4
var name = "window"
function Person (name) {
this.name = name
this.obj = {
name: "obj",
foo1: function () {
return function () {
console.log(this.name);
}
},
foo2: function () {
return () => {
console.log(this.name)
}
}
}
}
var person1 = new Person("person1")
var person2 = new Person('person2')
person1.obj.foo1()();//window
person1.obj.foo1.call(person2)()//window
person1.obj.foo1().call(person2)//person2
person1.obj.foo2()();//obj
person1.obj.foo2.call(person2)()//person2
person1.obj.foo2().call(person2)//obj
更详细的过程:
解析一:HTML解析过程
解析二:生成CSS规则
解析三:构建Render Tree
解析四:布局和绘制
defer属性
和样式表等外部资源可能并没有下载完毕。async属性
defer通常用于需要在文档解析之后操作DOM的JS代码,并且对多个script文件有顺序要求
async通常用于独立的脚本,对其他脚本、甚至DOM没有依赖的
V8引擎解析执行JS代码
V8引擎就是将高级语言代码最终转成二进制代码交给CPU执行
function sum(num1,num2) {
return num1 + num2
}
// 多次调用时,每次都需要将字节码翻译成机器指令,效率很低
// 于是有了TurboFan来收集信息,生成优化的机器码,之后重复调用时,直接运行优化的机器码就可以
sum(10,20)
sum(20,30)
// 当类型出现变化,优化的机器码会反优化成字节码,重新由字节码开始运行,翻译成机器指令在运行
// 这样会导致性能降低,所以在开发中尽量保证类型一致
// 所以TS在一定程度上可以提高JS性能
sum("10","20")
function sum(num1,num2) {
return num1 + num2
}
// 多次调用时,每次都需要将字节码翻译成机器指令,效率很低
// 于是有了TurboFan来收集信息,生成优化的机器码,之后重复调用时,直接运行优化的机器码就可以
sum(10,20)
sum(20,30)
// 当类型出现变化,优化的机器码会反优化成字节码,重新由字节码开始运行,翻译成机器指令在运行
// 这样会导致性能降低,所以在开发中尽量保证类型一致
// 所以TS在一定程度上可以提高JS性能
sum("10","20")
先了解两个概念
var name = "why"
var name = "why"
function foo() {
var name = 'foo'
console.log(name);
}
var num1 = 20
var num2 = 30
var result = num1 + num2
console.log(result)
foo()
认识VO对象:
var message = "Global Message"
var obj = {
name: "why"
}
function bar() {
console.log("bar function");
var address = "bar"
}
function foo(num) {
var message = 'Foo Message'
bar()
var age = 18
var height = 1.88
console.log("foo function")
}
foo(123)
执行过程:
var message = "Global Message"
,会在栈中执行并将值保存在栈中,但是在VO上也会添加一个属性message= “Global Message”;obj是引用类型,会在堆中创建一个obj对象,并将地址赋值给obj;foo(123)
会创建一个新的执行上下文,关联一个新的AO对象,AO对象中一样先将变量message、age、height赋值为undefined var message = "Global Message"
function foo() {
console.log(message);
}
var obj = {
bar: function () {
var message = "bar message"
//当这里foo被调用时,打印出来的message的值是Global Message
// 因为函数的作用域链在创建时就确定了
foo()
}
}
obj.bar()
var message = "hello world"
debugger
function foo() {
var message = "hello foo"
function bar() {
var message = "hello bar"
console.log(message);
}
return bar
}
var bar = foo()
bar()
// 1.面试题一:
var n = 100
function foo() {
// 这里访问的n变量就是全局的n
n = 200
}
foo()
console.log(n);//200
// 面试题二:
var n = 100
function foo(){
console.log(n);//undefined
var n = 200 //作用域提升
console.log(n)//200
}
foo()
// 3.面试题三:
var n = 100
function foo1() {
console.log(n);
}
function foo2() {
var n = 200
console.log(n)//200
foo1()//100
}
foo2()
// 面试题四:
var a = 100
function foo() {
console.log(a); //undefined
return //在代码执行时才return
var a = 100 //在代码解析时就创建了a
}
foo()
// 面试题五:
function foo() {
var a = b = 100
}
foo()
console.log(b)//100 因为不使用var创建变量时,会默认放到全局window中
console.log(a);//a is not defined
闭包就是嵌套函数,使用了外层作用域中的变量
如果嵌套函数被返回了出去被调用,一直存在,那么可能造成内存泄漏
本质上就是上级作用域内变量的生命周期,因为被下级作用域内引用,没有被释放
// 写一个闭包函数
function foo(){
const a = 2;
function bar () {
console.log(a)
}
return bar
}
let baz = foo()
baz()
function createAdder(count) {
function adder(num) {
return count + num
}
return adder
}
var adder5 = createAdder(5)
adder5(100)//105
var adder8 = createAdder(8)
adder8(30)//38
adder8 = null
function foo() {
var name = 'foo'
var age = 18
var height = 1.88
function bar() {
// 浏览器做的优化
// 因为这里没有用到age
// 所以浏览器会将age从内存中删除,以释放内存
console.log(name, height);
}
}
var fn = foo()
fn()
// 定义函数
function foo(a, b, c) {
}
var bar = function() {
}
// 自定义属性
foo.message = "hello foo"
console.log(foo.message);
// 默认函数对象中已经有自己的属性
// 1.name属性
console.log(foo.name)
// 2.length属性:参数(形参)个数
// 不算剩余参数
console.log(foo.length)
// 定义函数
function foo(m, n) {
// 类数组对象
console.log(arguments[3]);
for (var arg of arguments) {
console.log(arg)
}
}
var bar = function () {
}
foo(10, 20, 30, 40)
在开发中,我们经常需要将arguments转成Array,以便使用数组的一些特性
// 定义函数
function foo(m, n) {
// 类数组对象
console.log(arguments[3]);
// 将arguments转成数组方式一:
var newArguments = []
for (var arg of arguments) {
newArguments.push(arg)
}
console.log(newArguments)
// 将arguments转成数组方式二:ES6方法
// 传入可迭代对象
var newArgs1 = Array.from(arguments)
console.log(newArgs1)
// 使用扩展运算符
var newArgs2 = [...arguments]
console.log(newArgs2)
// 将arguments转成数组方式三:slice
var newArgs = [].slice.apply(arguments)
var newArgs3 = Array.prototype.slice.apply(arguments)
console.log(newArgs)
}
// 剩余参数:rest parameters
// 剩余参数需要写到参数列表最后
function foo(num1, num2, ...args) {
console.log(args);
}
foo(20, 30, 111, 222, 343)
// 普通的函数
function foo(x, y, z) {
console.log(x + y + z);
}
foo(10, 20, 30)
//柯里化函数
function foo1(x) {
return function(y) {
return function(z) {
console.log(x, y, z)
}
}
}
foo1(10)(20)(30)
// 箭头函数的写法
var foo3 = x => y => z => console.log(x, y, z)
foo3(10)(20)(30)
// 普通的函数
function foo(x, y, z) {
console.log(x + y + z);
}
function sum(num1, num2) {
return num1 + num2
}
function logInfo(data, type, message) {
console.log(`时间:${data} 类型:${type} 内容:${message}`)
}
// 自动转化柯里化函数
function myCurrying(fn) {
function curryFn(...args) {
// 两类操作
// 第一类操作:参数不够,操作返回一个新的函数,继续接受参数
// 第二类操作:参数够,直接执行fn的函数
if (args.length >= fn.length) {
// 执行第二类
return fn(...args)
// 考虑到this时的写法
// return fn.apply(this, args)
} else {
// 执行第一类
// 继续接受参数
return function (...newArgs) {
// 重新进行判断
return curryFn(...args.concat(newArgs))
//return curryFn.apply(this, args.concat(newArgs))
}
}
}
return curryFn
}
// 对其他函数进行柯里化
var fooCurry = myCurrying(foo)
fooCurry(10)(20)(30)
fooCurry(10, 20, 30)
var sumCurry = myCurrying(sum)
console.log(sumCurry(10)(20))
var sum5 = sumCurry(5)
console.log(sum5(10))
console.log(sum5(15))
console.log(sum5(18))
var logInfoCurry = myCurrying(logInfo)
logInfoCurry("2022", "9", '29')
logInfoCurry("2022")("9")("29")
var num = 100
// 第一步对数字*2
function double(num) {
return num * 2
}
// 第二步对数字**2
function pow(num) {
return num ** 2
}
console.log(pow(double(num)))
// 将上面两个函数组合在一起,生成一个新的函数
function composeFn(num) {
return pow(double(num))
}
console.log(composeFn(num))
var num = 100
// 第一步对数字*2
function double(num) {
return num * 2
}
// 第二步对数字**2
function pow(num) {
return num ** 2
}
console.log(pow(double(num)))
// 封装的函数:传入多个函数,自动将多个函数组合,挨个调用
function composeFn(...fns) {
// 1.边界判断
var length = fns.length
if(length <= 0) return
for (var i = 0; i < length; i++) {
var fn = fns[i]
if (typeof fn != "function") {
throw new Error(`fn ${i} must be function`)
}
}
// 2.返回的新函数
return function(...args) {
// 传递this和参数数组
var result = fns[0].apply(this, args)
for (var i = 1; i < length; i++) {
var fn = fns[i]
result = fn.apply(this, [result])
}
return result
}
}
var newFn = composeFn(double, pow)
console.log(newFn(100))
var obj = {
message: "hello world"
}
with (obj) {
console.log(message);//hello world
}
var codeString = `var message = 'hello world'; console.log(message);`
eval(codeString)//hello world
<script>
// 给整个script开启严格模式
"use strict"
// 给一个函数开启严格模式
function foo() {
"use strict"
}
script>
"use strict"
// 1.无法意外的创建全局变量
// function foo() {
// message = "hello world"
// }
// foo()
// console.log(message);
// 2.严格模式会引起静默失败(不报错也没有任何效果)的赋值操作抛出异常
var obj = {
name: "why"
}
Object.defineProperty(obj, "name", {
writable:false
})
obj.name = "kobe"
console.log(obj.name)
// 3. 参数名称不能相同
function foo(num, num) {
}
// 4. 不能以0开头
console.log(0o123)
// 5.eval函数不能为上层创建变量
eval(`var message = "hello world"`)
console.log(message)
// 6.严格模式下,this绑定不会转成对象类型
function foo() {
console.log(this)
}
foo.apply("abc")
foo.apply(123)
// 函数的独立调用在默认模式下this绑定widnow,严格模式下this绑定undefined
var obj = {
name: "why",
age: 18
}
Object.defineProperty(obj, "name", {
configurable: false ,//告诉js引擎,obj对象的name属性不可以被删除
enumerable:false,//不可枚举 (for-in/Object.keys)
writable: false,//不可以写入(只读属性)
value: "hello world"//返回这个value
})
delete obj.name
console.log(obj.name);//hello world
// 通过Object.defineProperty添加一个新的属性
Object.defineProperty(obj, "address", {})
delete obj.address
console.log(obj.address)//undefined, 没有被删掉
console.log(Object.keys(obj))//age
var obj = {
name: "why",
}
// 对obj对象中的name添加描述符(存取属性描述符)
var _name = ""
Object.defineProperty(obj, "name", {
configurable: true,
enumerable: true,
set: function(value) {
console.log('hello world', value);
_name = value
},
get: function() {
console.log('get方法')
return _name
}
})
obj.name = "kobe"
// 获取值
console.log(obj.name)
var obj = {
name: 'why',
age: 18,
height:1.88
}
// Object.defineProperty(obj, "name", {})
// Object.defineProperty(obj, "name", {})
// Object.defineProperty(obj, "name", {})
// 新增的方法
Object.defineProperties(obj, {
name:{
configurable:true,
enumerable:false,
},
age: {
},
height: {
}
})
var obj = {
name: 'why',
age: 18,
height: 1.88
}
// 获取对象的属性描述符
console.log(Object.getOwnPropertyDescriptor(obj, "name"));
console.log(Object.getOwnPropertyDescriptors(obj));
// 禁止对象扩展新属性:prevenExtensions
// 给一个对象添加新的属性会失败(在严格模式下会报错)
Object.preventExtensions(obj)
obj.address = "china"
console.log(obj.address)//undefined
// 密封对象,不允许配置和删除属性:seal
// 实际是调用preventExtendsions
// 并且将现有属性对的configurable:false
Object.seal(obj)
delete obj.name
console.log(obj.name)//why
// 冻结对象,不允许修改现有属性:freeze
// 实际上是调用seal
// 并且将现有属性的writable:false
Object.freeze(obj)
obj.name = "kobe"
console.log(obj.name)//why
var obj = {
name: "why",
age: 18
}
console.log(obj);
// 获取对象的原型
console.log(obj.__proto__)
console.log(Object.getPrototypeOf(obj))
console.log(Object.getPrototypeOf(obj) === obj.__proto__) //true
// 这个原型有什么用?
// 当我们通过[[get]]方式获取一个属性对应的value时
// 它会优先在自己的对象中查找,如果找到直接返回
// 如果没有找到,那么会在原型对象中查找
console.log(obj.name)
obj.__proto__.message = "hello world"
console.log(obj.message)//hello world
var obj = {}
function foo() {}
// 1.将函数看成是一个普通的对象时,它时具备__proto__(隐式原型)
// 为什么叫隐式原型,因为我们一般不会直接去获取它
// 作用:查找key对应的value时,会找到原型身上
console.log(obj.__proto__);
console.log(foo.__proto__);
// 2.将函数看作是一个函数时,它是具备prototype(显式原型)
// 作用,用来构建对象时,赋值给对象的隐式原型
// 对象没有prototype
console.log(foo.prototype)
console.log(obj.prototype)//undefined
function Foo() {
// 1.创建空的对象
// 将Foo的显式原型赋值给空对象的隐式原型
}
console.log(Foo.prototype);
var f1 = new Foo()
var f2 = new Foo()
console.log(f1.__proto__)
console.log(f2.__proto__)
console.log(f1.__proto__ === Foo.prototype)//true
function Student(name, age, sno) {
this.name = name
this.age = age
this.sno = sno
// 方式一:导致每次创建一个新对象时,都会创建这三个方法,这是没必要的
// this.running = function() {
// console.log(this.name + "running");
// }
// this.eating = function() {
// console.log(this.name + "eating");
// }
// this.studying = function() {
// console.log(this.name + "studying");
// }
}
// 方式二:
// 当我们多个对象拥有共同的值,我们可以将它放到构造函数的显式原型上
// 由构造函数创建出来的所有对象,都会分享这些属性
Student.prototype.running = function () {
console.log(this.name + "running");
}
// 1.创建三个学生
var stu1 = new Student('why1', 18, 111)
var stu2 = new Student('why2', 28, 112)
var stu3 = new Student('why3', 38, 113)
stu1.running()
// 函数对象中显式原型中重要的属性:constructor,指向Person函数对象
function Person() {
}
var PersonPrototype = Person.prototype
console.log(PersonPrototype);
console.log(PersonPrototype.constructor);
console.log(PersonPrototype.constructor === Person);//true
console.log(Person.name)
console.log(PersonPrototype.constructor.name)
// 2.实例对象
var p = new Person()
console.log(p.__proto__.constructor)
console.log(p.__proto__.constructor.name)
function Person() {
}
console.log(Person.prototype);
// 在原有的原型对象上添加新的属性
Person.prototype.message = "hello world"
Person.prototype.info = { name: 'hhh', age: 30 }
Person.prototype.running = function() {}
Person.prototype.eating = function() {}
// 直接赋值一个新的原型对象
// 弊端:没有指向Person的constructor属性
Person.prototype = {
message: "hello person",
info: { name: "hhh", age: 30},
running: function() {},
eating: function() {},
// 自定义指向Person的constructor属性
// constructor: Person
}
Object.defineProperty(Person.prototype, "construcotor", {
configurable: true,
writable: true,
enumerable:false,
value: Person
})
console.log(Object.keys(Person.prototype))
var p1 = new Person()
console.log(p1.message)//hello person
// 1.{}的本质
//var info = {}
// 相当于 var info = new Object()
//console.log(info.__proto__);
//console.log(info.__proto__ === Object.prototype);//true
//2. 原型链
var obj = {
name: "why",
age: 18
}
// 查找顺序
// 1.obj上面找到
// 2.obj.__proto__上找到
// 3.obj.__proto__.___proto__ => null 上查找
// console.log(obj.message)//undefined
//3.对现有的代码进行改造
obj.__proto__ = {
// message: "hello world"
}
obj.__proto__.__proto__ = {
message:'hello world'
}
console.log(obj.message)
Stu类实现继承Person类的属性和方法
方式一:父类的原型直接赋值给子类的原型(错误做法)
方式二:创建一个父类的实例对象(new Person()),用这个实例对象作为子类的原型
原型链继承的弊端:某些属性(Person类中的name和age属性)其实是保存在p对象上的
// 定义Person构造函数(类)
function Person(name, age) {
this.name = name
this.age = age
}
Person.prototype.running = function() {
console.log("running");
}
Person.prototype.eating = function() {
console.log('eating')
}
// 定义学生类
function Student(sno, score){
//this.name = name
// this.age = age
this.sno = sno
this.score = score
}
// 实现继承方式一:父类的原型直接赋值给子类的原型(错误做法)
// 缺点:父类和子类共享一个原型对象,修改了任意一个,另外一个也被修改
// Student.prototype = Person.prototype
// 方式二:创建一个父类的实例对象(new Person()),用这个实例对象作为子类的原型
var p = new Person("why", 18)
Student.prototype = p
// Student.prototype.running = function() {
// console.log("running")
// }
// Student.prototype.eating = function() {
// console.log('eating')
// }
Student.prototype.studying = function() {
console.log("studying")
}
// 创建学生
var stu1 = new Student("kobe", 18, 111, 110)
stu1.running()
stu1.studying()
console.log(stu1.name, stu1.age)
function Student(name, age, sno, score){
// 重点:借用构造函数
Person.call(this, name, age)
this.sno = sno
this.score = score
}
// 满足什么条件
// 1. 必须创建一个对象
// 2. 这个对象的隐式原型指向父类的显式原型
// 3. 将这个对象赋值给了类的显式原型
function Person() { }
function Student() { }
// 1.之前的做法
var p = new Person()
Student.prototype = p
// 2. 方案一
var obj = {}
// _proto_属性在有些浏览器可能不兼容
// obj._proto_ = Person.prototype
Object.setPrototypeOf(obj, Person.prototype)
Student.prototype = obj
// 3. 方案二:和之前的做法相比更具通用性,也更具兼容性
function F() { }
F.prototype = Person.prototype
Student.prototype = new F()
// 4. 方案三
// Object.create实现创建一个新对象,并且赋值一个原型对象
var obj3 = Object.create(Person.prototype)
Student.prototype = obj3
// 封装方案三:寄生组合式继承
// 寄生式函数,新建了一个对象,而不是直接使用父类的实例
function inherit(Subtype, Supertype) {
var obj = Object.create(Supertype.prototype)
Subtype.prototype = obj
//使用数据属性描述符添加construct属性
Object.defineProperties(Subtype.prototype, "contructor", {
enumerable: false,
configurable: true,
writable: true,
value: Subtype
})
}
// 如果担心Object.create有兼容性问题时,自定义createObject函数
function createObject(o) {
function F() { }
F.prototype = o
return new F()
}
//inde.html
<script src="./index.js">script>
<script>
function Person(name, age, height) {
this.name = name
this.age = age
this.height = height
}
Person.prototype.running = function() {
console.log("running");
}
Person.prototype.eating = function() {
console.log("eating")
}
function Student(name, age, height, sno) {
Person.call(this, name, age, height)
this.sno = sno
}
inherit(Student, Person)
Student.prototype.studying = function() {
console.log("studying")
}
var stu1 = new Student("why", 18, 1.88, 111)
console.log(stu1)
stu1.running()
stu1.eating()
stu1.studying()
script>
function Person() {}
function Student() {}
function Teacher() {}
inherit(Student, Person)
console.log(Person.prototype.__proto__ === Object.prototype);
// 在Object的原型上添加属性
Object.prototype.message = "hello world"
var stu = new Student()
console.log(stu.message)//hello world
// Object原型上已存的一些方法
console.log(Object.prototype)
console.log(stu.toString())
// ES6定义类
// {} :对象/代码块/类的结构
class Person {
}
// 创建实例对象
var p1 = new Person()
var p2 = new Person()
console.log(p1, p2);
// 表达式定义方法
var Student = class {
}
var stu1 = new Student()
console.log(stu1)
class Person {
// 1.类中的构造函数
// 当我们通过new关键字调用一个Person类时,默认调用class中的constructor方法
constructor(name, age) {
// 在constrotor中跟之前的写法一样
this.name = name
this.age = age
}
// 2.实例方法 (定义在类的显式原型上,只有实例对象可以使用)
running() {
console.log(this.name + "running")
}
}
var p1 = new Person("why", 18)
console.log(p1);
//Person类的显式原型也会赋值给实例对象p1的隐式原型
console.log(Person.prototype === p1.__proto__)//true
p1.running()
// 针对对象
// 方式一:描述符
var obj = {
_name: "why"
}
Object.defineProperty(obj, "name", {
configurable: true,
enumerable: true,
set: function () {
},
get: function () {
}
})
// 方式二:直接在对象中定义访问器(不推荐)
var obj = {
_name: "why",
set name(value) {
console.log("setter方法调用")
this._name = value
},
get name() {
console.log("getter方法调用")
return this._name
}
}
obj.name = "join"
console.log(obj.name)
class Person{
// 程序员约定俗成:以下划线开头的属性和方法,不在外界访问
constructor(name, age) {
this._name = name
this._age = age
}
set name(value) {
this._name = value
}
get name() {
return this._name
}
}
var p1 = new Person("why", 18)
p1.name = "kobe"
console.log(p1.name);
// 2.访问器应用场景
class Rectangle {
constructor(x, y, width, height) {
this.x = x
this.y = y
this.width = width
this.height = height
}
get position() {
return { x: this.x, y: this.y}
}
}
var react1 = new Rectangle(10, 20, 100, 200)
console.log(react1.position)
class Person {
// 实例方法
running() { }
// 类方法(静态方法):直接通过类来调用
static randomPerson() {
console.log('类方法被调用');
}
}
var p1 = new Person()
Person.randomPerson()
// 定义父类
class Person {
constructor(name, age) {
this.name = name
this.age = age
}
running() {
console.log("running")
}
eating() {
console.log("eating")
}
}
class Student extends Person {
constructor(name, age, sno, score) {
// this.name = name
// this.age = age
super(name, age)
this.sno = sno
this.score = score
}
studying() {
console.log('studying')
}
}
var stu1 = new Student("why", 18, 111, 12)
console.log(stu1)
stu1.running()
class Teacher extends Person {
constructor(name, age, title) {
this.name = name
this.age = age
this.sno = title
}
teaching() {
console.log("teaching")
}
}
super关键字:
super可以用在:子类的构造方法、实例方法、静态方法
// 继承内置类的应用场景
class MyArray extends Array {
get lastItem() {
return this[this.length - 1]
}
get firstItem() {
return this[0]
}
}
var arr = new MyArray(10, 20, 30)
console.log(arr.lastItem);
console.log(arr.firstItem);
function mixinAnimal(BaseClass) {
return class extends BaseClass {
running() {
console.log("running");
}
}
}
function mixinRunner(BaseClass) {
return class extends BaseClass {
flying() {
console.log('flying')
}
}
}
class Bird {
eating() {
console.log("eating")
}
}
var NewBird = mixinRunner(mixinAnimal(Bird))
var bird = new NewBird()
bird.flying()
bird.running()
bird.eating()
babel在线转化工具
//es6
class Person {
constructor(name, age) {
this.name = name
this.age = age
}
running() { }
static randomPerson() { }
}
let p1 = new Person()
//es5
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
// 如果不是Person的实例则报错,不允许构造函数直接调用
throw new TypeError("Cannot call a class as a function");
}
}
function _defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor);
}
}
function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
Object.defineProperty(Constructor, "prototype", { writable: false });
return Constructor;
}
function _toPropertyKey(t) {
var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i);
}
function _toPrimitive(t, r) {
if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) {
var i = e.call(t, r || "default");
if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value.");
} return ("string" === r ? String : Number)(t);
}
// 纯函数:相同的输入一定产生相同的输出,并且不会产生副作用
var Person = /*#__PURE__*/function () {
function Person(name, age) {
_classCallCheck(this, Person);
this.name = name;
this.age = age;
}
_createClass(Person, [{
key: "running",
value: function running() { }
}], [{
key: "randomPerson",
value: function randomPerson() { }
}]);
return Person;
}();
var p1 = new Person();
##Java中面向对象的多态表现
//继承之后操作统一的接口
class Shap {
getArea(){}
}
class Circle extends Shap {
constructor( radius ){
super()
this.radius = radius
}
getArea() {
return this.radius * this.radius
}
}
class Rectangle extends Shap{
constructor( width, height ) {
super()
this.width = width
this.height = height
}
getArea() {
return this.width * this.height
}
}
var react1 = new Rectangle(100, 200)
var c1 = new Circle(20, 30)
function getShapeArea(shape) {
console.log(shape.getArea())
}
getShapeArea(react1)
getShapeArea(c1)
// 多态的表现:JS到处都是多态
function sum(a1, a2) {
return a1 + a2
}
sum(20, 30)
sum("abc", "cba")
// 多态的表现形式二:
var foo = 123
foo = "hello world"
foo = {
}
foo = []
/*
1.属性的增强
2.方法的增强
3.计算属性的写法
*/
var name = "why"
var age = 18
var key = "address"
var obj = {
// 1.属性的增强
name,
age,
// 2.方法的增强
running: function() {
console.log(this)
},
swimming() {
console.log(this)
},
// 3.计算属性名
[key]: "安吉"
}
obj.swimming()
var names = ["a", "b", "c", "d"]
var obj = { name: "why", age: 18, height: 1.88 }
// 1.数组的解构
// 1.1基本使用
var [name1, name2, name3] = names
console.log(name1, name2, name3);
// 1.2顺序问题:严格的顺序
var [name1, , name3] = names
console.log(name1, name3)
// 1.3 解构出数组
var [name1, name2, ...newArray] = names
console.log(name1, name2, newArray)
// 1.4 解构的默认值
// 当name3没有赋值时,使用默认值w
var [name1, name2, name3 = "w"] = names
// 2.对象的解构
// 2.1. 基本使用
var { name, age, height } = obj
console.log(name, age, height)
// 2.2. 顺序问题:对象的解构是没有顺序的,根据key解构
var { height, name, age } = obj
console.log(name, age, height)
// 2.3.对变量进行重命名
var { height: height2, name: name2, age: age2 } = obj
console.log(name2, height2, age2)
// 2.4 默认值
var { height, name, age, address = "china" } = obj
console.log(name, age, height, address)
// 2.5 对象的剩余内容
var { name, age, ...newObj} = obj
console.log(newObj)
// 应用:在函数中(其他类似的地方)
function getPosition( {x, y}) {
console.log(x,y)
}
getPosition({ x: 10, y: 20})
function foo(name, age) {
console.log(this, name, age);
}
// 1.给函数对象添加方法:
Function.prototype.Myapply = function (thisArg, otherArgs) {
// this -> 调用的函数对象
// thisArg -> 传入的第一个参数,要绑定的this
// 1.获取thisArg,并且确保是一个对象类型
thisArg = (thisArg === null || thisArg === undefined) ? window : Object(thisArg)
// thisArg.fn = this
Object.defineProperty(thisArg, "fn", {
configurable: true,
value: this
})
// 检查 otherArgs 是否存在,如果不存在,设置为一个空数组
// if (!otherArgs) {
// otherArgs = [];
// }
thisArg.fn(...otherArgs || [])
delete thisArg.fn
}
foo.Myapply({ name: "why" }, ["james", 25])
foo.Myapply(123)
foo.Myapply(null)
Function.prototype.Mycall = function (thisArg, ...otherArgs) {
// 1.获取thisArg,并且确保是一个对象类型
thisArg = (thisArg === null || thisArg === undefined) ? window : Object(thisArg)
// thisArg.fn = this
Object.defineProperty(thisArg, "fn", {
configurable: true,
value: this
})
thisArg.fn(...otherArgs)
delete thisArg.fn
}
foo.Mycall({ name: "why" }, "james", 25)
foo.Mycall(123)
foo.Mycall(null)
function foo(name, age) {
console.log(this, name, age);
}
// 1.封装思想
// 1.1 封装到独立的函数中
function execFn(thisArg, otherArgs, fn) {
thisArg = (thisArg === null || thisArg === undefined) ? window : Object(thisArg)
Object.defineProperty(thisArg, "fn", {
configurable: true,
value: fn
})
thisArg.fn(...otherArgs || [])
delete thisArg.fn
}
// 1.2 封装到原型中
Function.prototype.Myexec = function (thisArg, otherArgs) {
thisArg = (thisArg === null || thisArg === undefined) ? window : Object(thisArg)
Object.defineProperty(thisArg, "fn", {
configurable: true,
value: this
})
thisArg.fn(...otherArgs || [])
delete thisArg.fn
}
// 1.给函数对象添加方法:
Function.prototype.Myapply = function (thisArg, otherArgs) {
// execFn(thisArg, otherArgs, this)
this.Myexec(thisArg, otherArgs)
}
foo.Myapply({ name: "why" }, ["james", 25])
foo.Myapply(123)
foo.Myapply(null)
Function.prototype.Mycall = function (thisArg, ...otherArgs) {
// execFn(thisArg, otherArgs, this)
this.Myexec(thisArg, otherArgs)
}
foo.Mycall({ name: "why" }, "james", 25)
foo.Mycall(123)
foo.Mycall(null)
console.log(message);//可以访问,但是值是undefined
// 1.var声明的变量会进行作用域的提升
var message = "hello world"
// 2.let/const声明的变量
console.log(address)//会报错,不能访问
let address = "china"
// 暂时性死区
function foo() {
// 暂时性死区begin
console.log(bar)
// 暂时性死区end
let bar = "bar"
let baz = "baz"
}
foo()
// 暂时性死区和定义的位置没有关系,和代码执行顺序有关系
function foo() {
console.log(message);
}
let message = "hello world"
foo()
console.log(message)
// 3.暂时性死区形成之后,在该区域中的这个标识符不能被访问
let message = "hello world"
function foo() {
console.log(message);//会报错,不会访问外部的message
let message = "hello let"
}
foo()
// 1.var定义的变量是会默认添加到window上的
var message = "hello world"
var address = "china"
console.log(window.message);
console.log(window.address)
// 2.let/const定义的变量不会添加到window上
let message = "hello world"
let address = "china"
console.log(window.message);
console.log(window.address)
模版字符串基本使用
const name = "why"
const age = 18
// 1.ES6之前
// const info = "my name is" + name + ", age is" + age
// 2.ES6之后
const info = `my name is ${name}, age is ${age}`
console.log(info);
标签模版字符串的使用
function foo(...args) {
console.log("参数", args);
}
foo("why", 18, 1.88)
// 标签模版字符串
foo`my name is ${name}, age is ${age}`
默认值的写法:
两种不严谨的写法
arg1 = arg1 ? arg1p: "默认值"
arg1 = arg1 || "默认值"
严谨的写法
arg1 = (arg1 === undefined || arg1 === null) ? "默认值": arg1
arg1 = arg1 ?? "默认值"
简便的写法:默认参数
function foo(arg1 = "默认值" ,arg2 = '默认值'){}
默认参数的写法注意:
基本使用:
// 基本使用
// ES6
const names = ["a", "b", "c", "d"]
const str = "hello"
function foo(name1, name2, ...args) {
console.log(name1, name2, args);
}
foo(...names)
foo(...str)
// ES9
const obj = {
name: "why",
age: 18
}
// 错误使用:在函数调用时,使用展开运算符,将对应的展开数据,进行迭代
// 可迭代对象:数组/string/arguments
// foo(...obj)
// 在构建对象字面量时使用展开运算符
const info = {
...obj,
height:1.88,
address: "china"
}
console.log(info)
const obj = {
name: "why",
age: 18,
height: 1.88
}
//1.引用赋值
const info1 = obj
//2.浅拷贝
const info2 = {
...obj
}
info2.name = "kobe"
// 深拷贝
const info3 = JSON.parse(JSON.stringify(obj))
const num1 = 100
const num2 = 0b100
const num3 = 0o100
const num4 = 0x100
const num5 = 100_000_000
const s1 = Symbol()
//加入对象中
const obj = {
[s1]: "aaa"
}
const s2 = Symbol()
obj[s2] = "bbb"
const s3 = Symbol()
Object.defineProperty(obj, s3, {
value: "aaa"
})
//获取Symbol对应的key
const keys = Object.getOwnPropertySymbols(obj)
for(const key of keys){
}
//如果相同的key,通过Symbol.for可以生成相同的Symbol值
const s5 = Symbol.for("ddd")
const s6 = Symbol.for("ddd")
console.log(s5 === s6) //true
//获取传入的key
Symbol.keyFor(s5)
// 创建Set
const set = new Set()
// 添加元素
set.add(10)
set.add(22)
set.add(35)
set.add(22)
console.log(set);
const info = {}
const obj = {}
set.add(info)
set.add(obj)
console.log(set);
// 3.应用场景:数组去重
const names = [ "a", "b", "a", "c", "d", "b"]
// 去重方式一:
// const newNames = []
// for( const item of names) {
// if(!newNames.includes(item)) {
// newNames.push(item)
// }
// }
// console.log(newNames)
// 去重方式二:
const newNamesSet = new Set(names)
const newNames = Array.from(newNamesSet)
console.log(newNames)
set.forEach(item => console.log(item))
const pwset = new WeakSet()
class Person {
constructor() {
pwset.add(this)
}
running() {
if(!pwset.has(this)) throw new Error("只能通过实例对象来调用running方法")
console.log("runnning",this)
}
}
Map基本使用
const info = { name: "why"}
const info2 = { age: 18}
// Map映射类型
const map = new Map()
map.set(info, "aaa")
map.set(info2, "bbb")
console.log(map);
// 1.应用场景一:对时间进行格式化
const minute = "2".padStart(2, "0")
const second = "6".padEnd(2, "0")
console.log(`${minute}:${second}`);
// 2.应用场景二:对一些敏感数据格式化
const cardNumber = "1222334455555"
cardNumber2 = cardNumber.slice(-4).padStart(cardNumber.length, "*")
console.log(cardNumber2)
// 1.flat()的使用:将一个数组按照指定的深度遍历,将遍历到的元素和子数组中的元素组成一个新的数组,进行返回
const nums = [10, 20, [111, 222], [333, 444], [[123, 321]], [231, 321]]
const newNum1 = nums.flat(1)
console.log(newNum1);
const newNum2 = nums.flat(2)
console.log(newNum2)
const messages = [
"hello world",
"hello flat",
"hello flatmap"
]
const finalMessage = messages.flatMap(item => item.split(" "))
console.log(finalMessage)
// 应用场景
const searchString = "?name=why&&age=18"
const params = new URLSearchParams(searchString)
console.log(params.get(name))
console.log(params.entries())
const paramObj = Object.fromEntries(params.entries())
console.log(paramObj)
const result = foo ?? "默认值"
const obj = {
name: "why",
frient: {
name: "kobe",
running: function() {
console.log("running");
}
}
}
// 这样使用不安全,不确定friend是否是undefined
obj.frient.running()
// 可选链的写法
// 想判断有没有再获取
obj?.frient?.running?.()
let obj = { name: "why" }
// 先创建对象
const registry = new FinalizationRegistry(value => {
console.log("对象被销毁了", value);
})
registry.register(obj, "obj")
obj = null
let obj = { name:"why"}
let info = new WeakRef(obj)
function foo(message) {
//message = message || "默认值"
message ||= "默认值"
// message = message ?? "默认值"
message ??= "默认值"
//message = message && message.name
message &&= message.name
}
const message = "my name is why"
//replace只替换第一个
const newMessage = message.replace("why", "kobe")
//replaceAll替换所有
const newMessage = message.replaceAll("why", "kobe")
name.at(-1)
name.at(1)
class Person {
//对象属性:public 公共的属性
height = 1.99
// 对象属性:private 私有:约定俗成
_intro = 'name'
// ES13 私有属性
#intro = "name"
//ES13 私有类属性
static #male = "70"
constructor(name, age) {
// 对象中的属性
this.name = name
this.age = age
}
// ES13 静态代码块
// 会自动执行一次,可以进行初始化操作
static {
console.log("hello world");
}
}
const keys = Objects.keys(obj)
for(const key of keys) {
let value = obj[key]
Object.defineProperty(obj, key, {
set: function(newValue) {
value = newValue
},
get: function() {
return value
}
})
}
const obj = {
name: "why",
age: 18,
height: 1.88
}
// 1. 创建一个Proxy对象
const objProxy = new Proxy(obj, {
// 编写捕获器
get: function(target,key) {
return target[key]
},
set: function(target, key, value) {
target[key] = value
},
deleteProperty: function(target, key) {
console.log("监听删除")
delete target[key]
},
has:function(target, key) {
console.log("监听判断")
return key in target
}
})
// 2.对obj的所有操作,去操作objProxy
console.log(objProxy.name);
objProxy.name = "lobe"
console.log(objProxy.name);
const obj = {
name: "why",
age: 18,
height: 1.88
}
// 1. 创建一个Proxy对象
const objProxy = new Proxy(obj, {
// 编写捕获器
get: function(target,key) {
return target[key]
},
set: function(target, key, value, receiver) {
// 不用直接操作原对象,而且返回布尔值,可以判断是否修改成功
Reflect.set(target, key, value)
// receiver是proxy对象
},
deleteProperty: function(target, key) {
console.log("监听删除")
delete target[key]
},
has:function(target, key) {
console.log("监听判断")
return key in target
}
})
objProxy.name = "lobe"
console.log(objProxy.name);
function execCode(counter, successCallback, failureCallback) {
// 异步任务
setTimeout(() => {
if (counter > 0) {
// ...
successCallback(counter)
} else {
failureCallback("error")
}
})
}
execCode(100, (value) => {
console.log(value);
}, (err) => {
console.log(err)
})
function execCode(counter) {
const promise = new Promise((resolve, reject) => {
// 异步任务
setTimeout(() => {
if (counter > 0) {
// ...
//对应then
resolve(counter)
} else {
//对应catch
reject("error")
}
})
})
return promise
}
execCode(100).then((value) => {
console.log(value);
}).catch((err) => {
console.log(err)
})
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("p1")
}, 3000)
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("p2")
}, 2000)
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("p3")
}, 5000)
})
Promise.all([p1, p2, p3]).then(res => {
console.log(res)
})
async function foo() {
console.log('1')
return 123
}
foo().then(res => {
console.log("res:", res)
})
async function fetchData() {
try {
const response = await fetch('https://example.com/api/data');
const data = await response.json();
return data;
} catch (error) {
console.error('Error:', error);
}
}
fetchData().then(result => {
console.log('Data:', result);
});
操作系统中:
JS是单线程的,但是JS的线程应该有自己的容器进程:浏览器或者Node
浏览器是一个进程吗?
JS的代码执行是在一个单独的线程中执行的:
但是耗时的操作,并不会由JS线程来执行
JS线程执行代码碰到setTimeout函数,会将计时操作交给浏览器线程执行,然后继续执行之后的js代码
当js代码执行完毕之后,浏览器的计时操作也结束,会将计时器结束的回调函数的js代码放到一个事件队列中
然后依次取出事件队列中的事件执行
try {
foo()
} catch (err) {
console.log(err)
} finally {
}
storage的基本使用:
let token = localStorage.getItem("token")
if (!token) {
console.log("从服务器获取");
token = "coderwhy"
localStorage.setItem("token", token)
}
let username = localStorage.getItem("username")
let password = localStorage.getItem("password")
if (!username || !password) {
console.log("用户输入账号密码")
username = "coderwhy"
password = "123456"
localStorage.setItem("username", username)
localStorage.setItem("password", password)
}
class Cache {
constructor(isLocal = true) {
this.storage = isLocal ? localStorage: sessionStorage
}
setCache(key, value) {
if(value) {
this.storage.setItem(key, JSON.stringify(value))
}
}
getCache(key) {
const result = this.storage.getItem(key)
if(result) {
return JSON.parse(result)
}
}
removeCache(key) {
this.storage.removeItem(key)
}
clear() {
this.storage.clear()
}
}
const localStorage = new Cache()
const sessionCache = new Cache(false)
// 手写防抖函数基本功能实现
function Mydebounce(fn, delay) {
// 用于记录上一次事件触发的timer
let timer = null
// 触发事件时执行的函数
const _debounce = () => {
// 取消上一次的事件
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
fn()
}, delay)
}
return _debounce
}
// 手写防抖函数基本功能实现
function Mydebounce(fn, delay) {
// 用于记录上一次事件触发的timer
let timer = null
// 触发事件时执行的函数
const _debounce = function (...args) {
// 取消上一次的事件
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
// 这里的this可以绑定输入框input,然后使用value值
fn().apply(this, args)
}, delay)
}
return _debounce
}
// 手写节流函数的时间差基本功能实现
function Mythrottle(fn, interval) {
let startTime = 0
const _throttle = function() {
const nowTime = new Date().getTime()
const waitTime = interval - (nowTime - startTime)
if(waitTime <= 0) {
fn()
startTime = nowTime
}
}
return _throttle
}
// 利用定时器实现节流
function throttle (fn, delay) {
let timer = null
return function (...arg) {
if(timer) return
timer = setTimeout(()=> {
fn.apply(this, arg)
timer = null
},delay)
}
}
// 手写deepClone深拷贝算法
function deepCopy(obj) {
if (typeof obj !== "object" || obj == null) {
return obj
}
let res = obj instanceof Array? [] : {}
// 通过for...in循环对象的所有枚举属性,然后再使用hasOwnProperty()方法来忽略继承属性
for (let key in obj) {
if(obj.hasOwnProperty(key)) {
res[key] = deepCopy(obj[key])
}
}
return res
}
// 判断一个标识符是否是对象类型
function isObject(value) {
const valueType = typeof value
return (value !== null) && ( valueType === "object" || valueType === "function")
}
function deepCopy(obj) {
// 1.如果是原始类型,直接返回
if (!isObject(obj)) {
return obj
}
// 2.如果是对象类型,才需要创建对象
const newObj = {}
for (const key in obj) {
newObj[key] = deepCopy(obj[key])
}
return newObj
}
const info = {
name: "coder",
age: 18,
friend:{
name:"why",
address: {
name: "洛杉矶",
detail: "斯坦普斯"
}
}
}
// 深拷贝
// 1.JSON方法
//当拷贝的数据为undefined,function(){}等时拷贝会为空
const obj4 = JSON.parse(JSON.stringify(info))
// 2.自己实现
const obj5 = deepCopy(info)
obj5.friend.name = "hello world"
console.log(info.friend.name);
console.log(obj5.friend.name);
// 手写实现事件总线
// 事件总线:我在这里点击触发,在那里执行
class MyEventBus {
constructor() {
this.eventMap = {}
}
on(eventName, eventFn) {
let eventFns = this.eventMap[eventName]
if (!eventFns) {
eventFns = []
this.eventMap[eventName] = eventFns
}
eventFns.push(eventFn)
}
emit(eventName) {
let eventFns = this.eventMap[eventName]
if (!eventFns) return
eventFns.forEach(fn => {
fn(...args)
})
}
off(eventName, eventFn) {
let eventFns = this.eventMap[eventName]
if (!eventFns) return
for (let i = 0; i < eventFns.length; i++) {
const fn = eventFns[i]
if (fn === eventFn) {
eventFns.splice(i, 1)
break
}
}
}
}
早期的网页都是通过后端渲染来完成的:服务器端渲染(SSR)
客户端发出请求 -> 服务端接受请求并返回HTML文档 -> 页面刷新,客户端加载新的HTML
文档
服务器端渲染的缺点:
在页面数据变动时,只向服务器请求新的数据,并且在阻止页面刷新的情况下,动态的替换页面中展示的数据?
// 1.创建XMLHTTPRequest对象
const xhr = new XMLHttpRequest()
// 2.监听状态的改变(宏任务)
xhr.onreadystatechange = function() {
// console.log(xhr.response);
//数据如果没有完全下载完成,直接返回
if (xhr.readyState !== XMLHttpRequest.DONE) return
// 将字符串转成JSON对象(JS对象)
const resJSON = JSON.parse(xhr.response)
console.log(resJSON)
}
// 3.配置请求
// method: 请求的方式(get/post/delete/put/patch...)
// url:请求的地址
xhr.open("get", "http://123.207.32.32:8000/home/multidata")
// 4.发送请求
xhr.send()
console.log(xhr.status)
console.log(xhr.statusText)
const xhr = new XMLHttpRequest()
xhr.onload = function () {
console.log(xhr.response);
}
xhr.responseType = "json"
// 传递方式一:数据暴露在url中
// xhr.open("get", "http://123.207.32.32:1888/02_param/get?name=why&age=18")
// 传递方式二:post -> urlencoded 数据放在请求体中
// xhr.open("post", "http://123.207.32.32:1888/02_param/posturl")
// xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded")
// xhr.send("name=why&age=18")
// 传递方式三:传递form表单中的数据
// const formData = new FormData(formEl)
// xhr.send(formData)
// 传递方式四:json
xhr.open("post", "http://123.207.32.32:1888/02_param/postjson")
xhr.setRequestHeader("Content-type", "application/json")
xhr.send(JSON.stringify({ name: "why", age: 18 }))
function Myajax({
url,
method = "get",
timeout = 10000,
headers = {},
success,
error,
data
} = {}) {
const xhr = new XMLHttpRequest()
xhr.onload = function () {
if (xhr.status >= 200 && xhr.status < 300) {
success && success(xhr.response)
} else {
error && error({ status: xhr.status, message: xhr.statusText })
}
}
xhr.responseType = "json"
if (method.toUpperCase() === "GET") {
const queryStrings = []
for (const key in data) {
queryStrings.push(`${key}=${data[key]}`)
}
url = url + "?" + queryStrings.join('&')
xhr.open(method, url)
xhr.send()
} else {
xhr.open(method, url)
xhr.setRequestHeader("Content-type", "application/json")
xhr.send(JSON.stringify(data))
}
}
//get请求
Myajax({
url: "http://123.207.32.32:8000/home/multidata",
success: function (res) {
console.log(res)
},
error: function (err) {
console.log(err)
}
})
// post请求
Myajax({
url: "http://123.207.32.32:1888/02_param/postjson",
method: "post",
data: {
name: "jsondata",
age: 22
},
success: function (res) {
console.log(res)
},
error: function (error) {
console.log(error)
}
})
function Myajax({
url,
method = "get",
timeout = 10000,
headers = {},
data
} = {}) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()
xhr.onload = function () {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(xhr.response)
} else {
reject({ status: xhr.status, message: xhr.statusText })
}
}
xhr.responseType = "json"
if (method.toUpperCase() === "GET") {
const queryStrings = []
for (const key in data) {
queryStrings.push(`${key}=${data[key]}`)
}
url = url + "?" + queryStrings.join('&')
xhr.open(method, url)
xhr.send()
} else {
xhr.open(method, url)
xhr.setRequestHeader("Content-type", "application/json")
xhr.send(JSON.stringify(data))
}
})
}
//get请求
Myajax({
url: "http://123.207.32.32:8000/home/multidata",
}).then(res => {
console.log(res)
}).catch(err => {
console.log(err)
})
//监听过期
xhr.ontimeout = function() {
console.log("请求过期:timeout")
}
//timeout:浏览器达到过期时间还没有获取到对应的结果时,取消本次请求
xhr.timeout = 3000
xhr.abort()
// 1.fetch发送get请求
// 优化方式一:
fetch("http://123.207.32.32:8000/home/multidata").then(res => {
const response = res
return response.json()
}).then(res => {
console.log(res)
}).catch(err => {
console.log(err)
})
// 优化方式二:
// async function getData() {
// const response = await fetch("http://123.207.32.32:8000/home/multidata")
// const res = await response.json()
// console.log(res)
// }
// getData()
// post请求并且有参数
async function getData() {
const response = await fetch("http://123.207.32.32:8000/home/multidata", {
method: "post",
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
body: {
name: "why",
age: 18
}
})
const res = await response.json()
console.log(res)
}
getData()
<body>
<input type="file" class="file">
<button class="upload">上传文件button>
<script src="./index.js">script>
<script>
const uploadBtn = document.querySelector('.upload')
uploadBtn.onclick = function () {
const xhr = new XMLHttpRequest()
xhr.onload = function () {
console.log(xhr.response)
}
xhr.responseType = "json"
xhr.open("post", "http://123.207.32.32:1888/02_param/upload")
// 表单
const fileEl = document.querySelector(".file")
const file = fileEl.files[0]
const formDate = new FormData()
formDate.append("avatar", file)
xhr.send(formDate)
}
script>
body>
const uploadBtn = document.querySelector('.upload')
uploadBtn.onclick = async function () {
// 表单
const fileEl = document.querySelector(".file")
const file = fileEl.files[0]
const formDate = new FormData()
formDate.append("avatar", file)
const response = await fetch("http://123.207.32.32:1888/02_param/upload", {
body: formDate
})
const res = await response.json()
console.log(res)
}