Set
Set是新的引用型的数据结构 它类似于数组,但是成员的值都是唯一的,没有重复的值。 Set本身是一个构造函数,用来生成 Set 数据结构。 Set函数可以接受一个数组作为参数,用来初始化。
回顾之前的数据结构 一般类型 Number String Boolean Undefined Null 以及前面新学习的 Symbol 引用类型 Object 基本用法
特性
1 内部数据都是唯一的,不会有重复的
const set = new Set([1, 2, 3, 4, 4]); console.log(set); // Set(4) {1, 2, 3, 4} // 内部数据都是唯一的,不会有重复的 ================================================ let mySet = new Set(); mySet.add(1); // Set(1) {1} mySet.add(5); // Set(2) {1, 5} mySet.add(5); // Set(2) {1, 5} 这里体现了值的唯一性 mySet.add("some text"); // Set(3) {1, 5, "some text"} 这里体现了类型的多样性 var o = {a: 1, b: 2}; mySet.add(o); mySet.add({a: 1, b: 2}); // Set(5) {1, 5, "some text", {…}, {…}} // 这里体现了对象之间引用不同不恒等,即使值相同,Set 也能存储
2 Set是新的引用类型的数据结构
// 先想一想 以前怎么判断是数组还是对象的? let arr = [1,2,3] let obj = {name:2} let res = arr instanceof Array let resOb = obj instanceof Object console.log(res); // true console.log(resOb); //true let resArr = Object.prototype.toString.call(arr) let resObj = Object.prototype.toString.call(obj) console.log(resArr); // [object Array] console.log(resObj); // [object Object] // 可以类比着来判断Set数据类型 let set = new Set([1,2,3,4]) let res = set instanceof Set let resSet = Object.prototype.toString.call(set) console.log(res); // true console.log(resSet); // [object Set]
3 类型转换
(通过... 或者 Array.from())
(不知道为什么没关系先记住 后面讲到了 iterator 遍历器 for...of 循环就知道了 其实就是因为我们的Set数据结构支持for of循环(内部部署了iterator接口))
const set = new Set([1, 2, 3, 4, 4]); const s = Array.from(set) console.log(s) // (4) [1, 2, 3, 4] ============================================== const s = [...set] console.log(s) // (4) [1, 2, 3, 4,] =============================================== 类型转换 // Array 转 Set var mySet = new Set(["value1", "value2", "value3"]); // 用...操作符,将 Set 转 Array var myArray = [...mySet]; String // String 转 Set var mySet = new Set('hello'); // Set(4) {"h", "e", "l", "o"} // 注:Set 中 toString 方法是不能将 Set 转换成 String
我们的数组去重可以怎么做?
(可以通过上述来实现)
Set数据内部判断值的机制 Set 内部判断两个值是否不同,使用的算法它类似于精确相等运算符(===)
set的方法
const set = new Set([1, 2, 3, 4, 4]); set.add(6); // 添加到最后一个,只能添加到末尾 console.log(set) // set(5)123446 console.log(set.size) // 有几个不重复元素,5 console.log(set.has(1)) // 是否含有1 ,true
5和"5"是两个不同的值 特殊情况就是NaN的情况 虽然NaN === NaN 返回false(其实NaN == NaN也会返回false) 但是在Set数据内部认为相等
add其实就是向Set数据里面添加内容
let set = new Set(); let a = NaN; let b = NaN; set.add(a); set.add(b);set // Set {NaN}
上面代码向 Set 实例添加了两次NaN,但是只会加入一个。这表明,在 Set 内部,两个NaN是相等的。 接着举例子两个对象总是不相等的。(因为{} === {} 返回false 其实 {}=={}也是返回false)
let set = new Set(); set.add({}); set.add({}); console.log(...set);// 此时有两项 {} {}
set实例的属性和方法 属性size
let set = new Set(); s.add(1).add(2).add(2); // 注意2被加入了两次 s.size // 2
方法 add delete has clear
let set = new Set(); s.add(1).add(2).add(2); // 注意2被加入了两次 s.size // 2 s.has(1) // true s.has(2) // true s.has(3) // false s.delete(2); // s.has(2) // false
方法forEach() Set 结构的实例与数组一样,也拥有forEach方法,用于对每个成员执行某种操作,没有返回值。
let set = new Set([1, 4, 9]); set.forEach((value, key) => console.log(key + ' : ' + value))
Set和展开运算符...实现数组的并集 交集 差集 走代码
let arr1 = [1,2,3] let arr2 = [4,3,2] let a = new Set(arr1); let b = new Set(arr2); //并集 let union = new Set([...a, ...b]); Set {1, 2, 3, 4} // 交集 let intersect = new Set([...a].filter(x => b.has(x))); set {2, 3} // 差集 let difference = new Set([...a].filter(x => !b.has(x))); Set {1}
Map
Map是什么? ES6 提供的另一种新的引用类型的数据结构 它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键) 以前引用类型中的对象也是键值对的集合 但是键限于字符串 总结起来就是 Object 结构提供了“字符串—值”的对应, Map 结构提供了“值—值”的对应,是一种更完善的 “键值对” 结构实现。 如果你需要“键值对”的数据结构,Map 比 Object 更合适
创建map
两种方法 // 二维数组 let m1 = new Map([ ["name","lwl"], ["age",23] [{a:1},"helan"] ]) console.log(m1) ====================== let m2 = new Map() m2.set("name","lwl") m2.set("age",23) console.log(m2) for (let i of m2){ consoole.log(i) } consoole.log([...m2]) // map结构转换为二维数组 =============================
怎么声明Map数据结构
const m = new Map(); const o = {p: 'Hello World'}; m.set(o, 'content') m.get(o) // "content" m.has(o) // true m.delete(o) // true m.has(o) // false
证明是引用类型数据
let map = new Map() let res = map instanceof Map let resMap = Object.prototype.toString.call(map) console.log(res); // true console.log(resMap); // [object Map]
和之前的对象进行比较 之前用对象作为键的时候
let o1 = {a:1} let o2 = {b:1} let o3 = {} o3[o1] = 1 o3[o2] = 2 console.log(o3) // [object Object]: 2 Object.keys(o3).map((v,i)=>{ console.log(v); // [object Object] console.log(i); // 0 console.log(typeof v); // string })
上面说明是会被覆盖的 此时用Map实现
let o1 = {a:1} let o2 = {b:1} let o3 = new Map() o3.set(o1,'123') o3.set(o2,'234') console.log(o3); console.log(o3.get(o1)); console.log(o3.get(o2));
接受数组作为参数 作为构造函数,Map 也可以接受一个数组作为参数。注意该数组的成员是一个个表示键值对的数组。
const map = new Map([ ['name', '张三'], ['title', 'Author'] ]); map.get('name') // "张三" map.get('title') // "Author" let m = new Map([ [123,'abc'], ['a',{x:1,y:2}], [3>1,666] ]); console.log(m.get(true)); // 666
传址特点--对象作为键名,传输的是地址,所以下例中的两个{x:1}虽然长相一样,但实际值不同(存储在堆内存的两个位置)
let m = new Map([ [123,'abc'], [{x:1},'cdf'],]); console.log(m.get({x:1}));-->undefined // 任意两个对象不相等,所以获取不到cdf // 可以修改成下面形式 let obj = {x:1}; let m = new Map([ [123,'abc'], [obj,'cdf'],]); console.log(m.get(obj));-->cdf
Map构造函数接受数组作为参数的本质 内部机制 下面代码牵扯到数组解构的模式匹配 就是forEach的时候可以问问同学们是否懂?
const items = [ ['name', '张三'], ['title', 'Author'] ]; const map = new Map(); items.forEach( ([key, value]) => map.set(k
方法
Map自带的一些方法 keys() values() entries(),返回的是迭代器,需要用 for of
//2.创建一个Map(可以区分两个对象obj_2,obj_1) const map =new Map([ ['name','张三'], ['age',18], ['sex','男'], [obj_1,'天空'], [obj_2,'大海'] ]); console.log(map); console.log(map.size); map.set('pet',['哈士奇','阿拉斯加']); console.log(map); console.log(map.get('pet')); console.log(map.get(obj_2)); console.log(map.delete(obj_2)); console.log(map.keys()); console.log(map.values()); // 对键值对的遍历 console.log(map.entries()); let res = map.entries() for(i of res){ console.log(i) } //3.forEach遍历 map.forEach(function(value,index){ console.log(index+':'+value); })
map参数的扩展 任何具有 Iterator 接口、且每个成员都是一个双元素的数组的数据结构 都可以当作Map构造函数的参数
const set = new Set([ ['foo', 1], ['bar', 2] ]); const m1 = new Map(set); m1.get('foo') // 1 const m2 = new Map([['baz', 3]]); const m3 = new Map(m2); m3.get('baz') // 3
箭头函数和方法扩展
箭头函数和普通函数的区别
-
this指向的问题
箭头函数的 this
指向是在定义函数时确定的,并且继承自外部作用域。具体来说,箭头函数的 this
绑定取决于包含它的最近一层非箭头函数作用域的 this
值。
箭头函数本身是没有this的,他的this是从他作用域链的上一层继承来的,并且无法通过call和apply改变this指向
// 第一题 var fn = function () { return () => { console.log(this.name) } } var obj1 = { name: '张三' } var obj2 = { name: '李四' } var name = '王五' obj1.fn = fn obj2.fn = fn obj1.fn()() // 3 obj2.fn()() // 4 fn()() // 5 // 第二题 var user = { name: '张三', fn: function () { var obj = { name: '李四' } var f = () => this.name return f.call(obj) } }
-
不能作为构造函数 没有prototype属性
var fn = ()=>{} console.log(fn.prototype) // undefined
-
没有arguments对象
var foo = () =>{ console.log(arguments) } foo(1,"23",true) // 如果是function会输出参数对象,但是箭头函数没有输出 ==================== // 这样拿到参数 var foo = (...arguments) =>{ console.log(arguments) } foo(1,"23",true)
-
不能使用yield命令,因此箭头函数不能用作 Generator 函数
箭头函数的实际运用
箭头函数相关面试题
// 对象没有作用域,this的指向是其定义位置的作用域
var name = 'window' var obj = { name: 'obj', methods: () => { console.log(this.name) }, fn: function (cb) { cb() } } obj.fn1 = function () { // 此处的this指向和外面的普通函数的指向相同,是window obj.fn(() => { console.log(this.name) }) } var fn1 = obj.fn1 obj.methods() // window,this的指向是其定义位置(写在哪)的作用域,但是obj对象没有作用域,所以此处指向的是全局 obj.fn(() => { console.log(this.name) }) // window,写在函数调用处,此时还是全局作用域。 fn1() // window obj.fn1() // obj //fn1() 和obj.fn1的区别 在 fn1() 的调用中,函数执行的上下文(即 this 的值)取决于函数被调用的方式。在这种情况下,由于 fn1 是在全局作用域中定义的,并且直接通过函数名进行调用,因此执行上下文为全局对象(在浏览器环境中是 window)。而在 obj.fn1() 的调用中,函数被作为 obj 对象的方法调用,因此执行上下文为 obj 对象本身。
Promise
Promise 是异步编程的一种解决方案
const promise = new Promise(function(resolve, reject) { if (/* 异步操作成功 */){ resolve(value); } else { reject(error); } })
Promise的三种状态
待定(pending): 初始状态,既没有被兑现,也没有被拒绝。 已兑现(fulfilled): 意味着操作成功完成。 已拒绝(rejected): 意味着操作失败。
// pending new Promise((resolve, reject) => {}) // fulfilled new Promise((resolve, reject) => { resolve('hello world') }) // rejected new Promise((resolve, reject) => { reject('bad code') })
Promise的状态一旦状态改变,就不会再变
// 思考这里的打印结果 new Promise((resolve, reject) => { reject('bad code') resolve('hello world') }).then(val => { console.log(val) }).catch(err => { console.log(err) }) // bad code
finally
// 当promis的状态发生变化就会触发finally new Promise((resolve, reject) => { reject('bad code') resolve('hello world') }).then(val => { console.log(val) }).catch(err => { console.log(err) }).finally(() => { console.log("promise状态变更了") })// promise状态变更了 ====================================== new Promise((resolve, reject) => { }).finally(() => { console.log("promise状态变更了") })// 没有输出
思考题:使用Promise,实现一个计数器将输入的2个数字相加,在间隔1s后,将所得结果再进行下一次计算
function counter (x,y,wait){ return new Promise( function(resolve,reject){ setTimeout(function(){ resolve(x+y) },1000) }) } counter(1,2,1000).then(val=>{ console.log(val) return conter(val,1,1000) }).then(num=>{ console.log(num) })
promise相关的方法
Promise.resolve()
Promise.resolve()方法会返回一个状态为fulfilled的promise对象。
可以用此方法直接创建一个对象,想获取内容就用.then
Promise.resolve(2).then((val) => { console.log(val) }) // Promise { < fulfilled >:2 }
Promise.reject()
Promise.reject()方法返回一个带有拒绝原因的Promise对象。
Promise.reject({ message: '接口返回错误' }).catch((err) => { console.log(err) })
Promise.all()
Promise.all() 方法接收一个promise的iterable类型(注:Array,Map,Set都属于ES6的iterable类型),返回一个promise实例。
const promise1 = Promise.resolve(3); const promise2 = 42; const promise3 = new Promise((resolve, reject) => { setTimeout(() => { resolve('hello') }, 1000); }); const promise4 = new Promise((resolve, reject) => { setTimeout(() => { resolve('world') }, 2000); }); // 需要等所有的promise对象都返回后(4等待两秒),才打印 Promise.all([promise1, promise2, promise3, promise4]).then((values) => { console.log(values); });// (4)[3,42,'hello','world']
面试题
function fn () { return new Promise((resolve) => { console.log('Promise1') fn1() setTimeout(() => { console.log('Promise2') resolve() console.log('Promise3') }, 0); }) } async function fn1() { var p = Promise.resolve().then(() => { console.log('Promise6') }) await p.then(() => { console.log('Promise7') }) console.log('end') } console.log('script') setTimeout(() => { console.log('setTimeout') }, 0) fn().then(() => { console.log('Promise4') })
async函数
async的出现让我们可以用一种更简洁的方式写出基于Promise的异步行为
function p () { return new Promise(resolve => { setTimeout(() => { resolve('异步结果') }, 1000) }) } // .then调用 function fn(){ p().then(val => { console.log(val) }) } fn() // async async function fn () { const res = await p() console.log(res) }
async函数的返回值为一个promise,通过then和catch来捕获内部的返回值
async function fn () { return 'hello world' }
特性
-
async函数内部会返回一个promise对象,如果看起来不是promise,那么它将会隐式的包装在promise中
async function fn () { return 'hello world' // 调用fn(),返回一个promise对象,状态是已完成,内容是hello world // 等价于 return Promise.resolve('hello world' ) } console.log(fn())
-
await能获取到promise状态改变后的值,如果后面不是一个promise,await 会把该值转换为已正常处理的Promise
async function fn () { await 1 // 不是一个promise,所以 await 会把该值转换为已正常处理的Promise } console.log(fn()) // 返回一个promise对象,状态是已完成,内容是undefine // 所以await 1 等价于 Promise.resolve(1).then(()=>undefined)
-
await后面promise的状态是reject,则await后的代码不会执行,async函数将返回状态为reject的promise
async function fn () { await Promise.reject('出错了') console.log(2) // 不会执行 } fn()
-
async函数内部如果存在await,await表达式会暂停整个async函数的执行,等当前位置promise状态改变后才能恢复(重要)
async function fn () { setTimeout(function () { console.log(1) }, 0) Promise.resolve().then(() => console.log(4)) await setTimeout(function () { console.log(5) }, 0) await Promise.resolve().then(() => console.log(6)) Promise.resolve().then(() => console.log(7)) console.log(3) } fn() //微任务 宏任务 4 6 3 7 1 5
Proxy
Proxy
是 JavaScript 的一个内置对象,它允许你创建一个代理对象,用于拦截并自定义目标对象上的操作。通过使用 Proxy
,你可以在目标对象的操作之前和之后执行自定义的逻辑,从而实现对目标对象的行为进行拦截、修改或增强。
Proxy
对象的创建方式如下:
javascript
const proxy = new Proxy(target, handler); // 创建一个可撤销的代理对象 const { proxy: p, revoke } = Proxy.revocable(data, handler)
-
target: 要使用 Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。
-
handler: 一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 p 的行为。定义了在进行拦截时的一些操作
基本语法
let obj ={} // get set拦截操作 let proxy = new Proxy(obj,{ get(target,key){ console.log("get",target[key]) return target[key] }, Set(target,key,value){ console.log("set",target,key,value) target[key]=value }) proxy.name = "lwl", // 赋值 proxy.name // 查看
handler
对象可以定义多个拦截器方法(也称为"陷阱"),用于拦截不同的操作,例如读取属性、写入属性、调用函数等。以下是一些常用的拦截器方法:
-
get(target, property, receiver)
:拦截对目标对象属性的读取操作。 -
set(target, property, value, receiver)
:拦截对目标对象属性的写入操作。 -
apply(target, thisArg, argumentsList)
:拦截对目标对象的函数调用。 -
has(target, property)
:拦截in
操作符,判断属性是否存在于目标对象中。 -
deleteProperty(target, property)
:拦截删除目标对象属性的操作。
proxy实现的功能
-
验证对象的传值
// 需要实现的功能 // 1. 验证age属性的数据类型是否为整数 // 2. 验证值的范围是否小于等于200 const validtor = { set(obj,prop,value){ if(prop === 'age'){ if(!Number.isIntger(value)){ throw new TypeError('类型错误,非整数') } } if(value>130){ throw new RangeError('设置范围错误') } } } let obj = {} const proxy = new Proxy{ obj ,validtor} proxy.age = 100
-
通过属性查找数组中的特定对象
var data = [ { name: 'Firefox' , type: 'browser' }, { name: 'SeaMonkey' , type: 'browser' }, { name: 'Thunderbird', type: 'mailer' } ] // 需要实现的功能 // 1. 通过索引返回对应数据 proxy[0] // 2. 通过number属性返回数组长度 proxy.number // 3. 通过name获取对应的数据 proxy['Firefox'] // 4. 通过type返回对应的数据 proxy['browser'] // 5. 通过types返回data中的type products.types const proxy = new Proxy(data,{ get (obj,prop){ if(prop in obj){ return obj[prop] } if(prop === 'number'){ return obj.length } } }) console.log(proxy[0]) console.log(proxy['length'])
Class类
使用构造函数生成实例
function Car(make, model) { this.make = make; this.model = model; } Car.prototype.getCarInfo = function() { return this.make + ' ' + this.model; }; const car = new Car('Toyota', 'Camry'); console.log(car.getCarInfo()); // 输出: Toyota Camry
使用类生成实例
// 构造器内写属性
javascript
class Car { constructor(make, model) { this.make = make; this.model = model; } getCarInfo() { return this.make + ' ' + this.model; } } const car = new Car('Toyota', 'Camry'); console.log(car.getCarInfo()); // 输出: Toyota Camry
深入了解class的特性
-
class的数据类型是一个函数
typeof class A {} function {}
-
class的原型的constructor指向class
class A {} A.prototype.constructor === A // true
-
通过 new 关键字创建出的实例的constructor指向该class
class A {} var a = new A() a.constructor === A // true
-
class内部的方法实际上都是定义在类的prototype上
class A { fn () {} toString() {} } var a = new A()
-
通过类创建对象的本质是调用类的constructor,如果类未定义constructor,则会在使用时默认添加
class A { } // 等同于 class A { constructor() {} }
-
class不能直接调用,需要通过new关键字
-
class内部方法指向的是实例,class内部是严格模式
class Logger { printName(name = 'world') { this.print(`Hello ${name}`); } print(text) { console.log(text); } } // 注意方法如果单独使用会报错,class内部是严格模式,所以 this 实际指向的是undefined const logger = new Logger(); const { printName } = logger; printName(); // 如果想要可以正常调用,可以使用箭头函数 class Logger { printName = (name = 'world') => { this.print(`Hello ${name}`); } print = (text) => { console.log(text); } } const logger = new Logger(); const { printName } = logger; printName();
构造函数与class的区别?
class的其他语法
取值函数(getter)和存值函数(setter)
class A { get name () { return '1' } set name (value) { console.log('setter:'+value) } }
类的属性名可以动态设置
var methodName = 'test' class A { [methodName] () {} } new A()
静态方法/属性 通过在属性和方法前添加static关键字,静态方法和属性不会被实例继承
// 静态方法/属性 function fn(){} fn.a = 1 fn.b = function(){} var f = new fn() // a和b时fn的静态属性,不会继承到f身上 =============================== class A { static fn () { this.getValue() // 静态方法里面的this指向的是类**而不是实例** } static getValue () { console.log('张三') } getValue() { console.log('李四') } } var a = new A() A.getValue() // 张三 a.getValue() // 李四 ================================ class A {} A.a = 1 A.fn = function () {} class A { static a = 1 static fn () { } }
静态方法:类可以定义静态方法,这些方法不会被实例继承,而是直接通过类访问。构造函数无法定义静态方法。
// 类 class MathUtils { static multiply(a, b) { return a * b; } } MathUtils.multiply(2, 3); // 输出 6 //由于它是一个静态方法,我们可以直接通过类名 MathUtils 来调用它,而不需要实例化类 // 构造函数 function MathUtils() { // ... } MathUtils.multiply = function(a, b) { return a * b; }; MathUtils.multiply(2, 3); // 抛出错误:MathUtils.multiply is not a function ```
定义实例的属性
class A { a = 1 b = 'SUCCESS' } // 等价于 class A { constructor(){ this.a = 1 this.b = 'SUCCESS' } }
get
和 set
关键字
et
和 set
关键字用于定义属性的获取器(getter)和设置器(setter)。它们提供了对类属性的访问和修改的控制,并允许在对属性进行读取和赋值时执行自定义的逻辑。
下面是一个例子,演示了在类中使用 get
和 set
的用法:
javascript
class Circle { constructor(radius) { this._radius = radius; // 使用下划线前缀表示私有属性 } get radius() { return this._radius; } set radius(value) { if (value <= 0) { throw new Error('Radius must be a positive number.'); } this._radius = value; } get area() { return Math.PI * this._radius ** 2; } } const circle = new Circle(5); console.log(circle.radius); // 输出: 5 circle.radius = 7; console.log(circle.radius); // 输出: 7 console.log(circle.area); // 输出: 153.93804002589985 circle.radius = -2; // 抛出错误: Radius must be a positive number.
class的继承
类继承允许子类继承父类的属性和方法,并且子类可以添加、重写或扩展这些属性和方法。
class Animal { constructor(name) { this.name = name; } speak() { console.log(`${this.name} makes a sound.`); } } class Dog extends Animal { constructor(name, breed) { super(name); this.breed = breed; } speak() { console.log(`${this.name} barks.`); } } const dog = new Dog('Buddy', 'Labrador'); dog.speak(); // 输出: Buddy barks. 我们定义了一个基类 Animal,它有一个构造函数和一个 speak 方法。然后,我们定义了一个子类 Dog,使用 extends 关键字继承了 Animal。子类 Dog 通过 super 关键字调用父类的构造函数,并添加了自己的属性 breed 和重写的 speak 方法。 通过创建 Dog 类的实例,我们可以调用继承自 Animal 类的 speak 方法,并输出特定于 Dog 类的行为。
css
盒子模型
标准盒子模型:宽度=内容的宽度(content)+ border + padding + margin
.container { width: 200px; height: 100px; background-color: black; padding: 20px; margin:20px; border: 1px solid blue; } 此时的宽度就是:200+20+20+1
低版本IE盒子模型:宽度=内容宽度(content+border+padding)+ margin
会压缩内容的宽度
习题
Set的数据类型及特性?
set是引用数据类型,特性是其内部数据都是唯一的,不会有重复的
Map的数据类型及特性?
map是引用数据类型, 它是键值对的集合,但是“键”的范围不限于字符串,可以是各种类型的值(包括对象)
iterator接口的作用?
提供了统一访问对象的方法,for...of
通过实现 Iterator 接口,对象可以成为可迭代对象,从而支持使用 for...of 循环和其他迭代器的操作。
for of与for in的区别?
for...of:遍历的是对象的值或元素本身,iterator接口
for...in:遍历的是对象的键或属性名。
箭头函数与普通函数的区别?
1 箭头函数没有自己的 this,它会继承外部作用域的 this 值
2 箭头函数不能用作构造函数,没有prototype属性,不能使用 new 关键字实例化一个箭头函数
3 箭头函数没有自己的 arguments
对象
箭头函数的this的指向?
全局作用域中的箭头函数:
const arrowFn = () => { console.log(this); }; arrowFn(); // 输出为全局对象( window)
对象方法中的箭头函数:
javascript
const obj = { name: 'John', sayHello: () => { console.log(this.name); } }; obj.sayHello(); // 输出 undefined,因为箭头函数捕获的是外部作用域(全局作用域)的 this 值
构造函数中的箭头函数:
function Person() { this.name = 'John'; this.sayHello = () => { console.log(this.name); }; } const john = new Person(); john.sayHello(); // 输出 John,箭头函数继承了构造函数中的 this 值
代码的执行结果是什么?
var obj = { say: function() { var f1 = () => { console.log(this); } f1(); }, pro: { getPro:() => { console.log(this); } } } var o = obj.say; o(); // Window obj.say(); // obj obj.pro.getPro(); // Window
var a = 10 var obj = { a: 20, say: () => { console.log(this.a) } } obj.say() // 10 var anotherObj = { a: 30 } obj.say.apply(anotherObj) // 10
async function fn () { setTimeout(() => { console.log(4) }, 0) Promise.resolve().then(() => { console.log(1) }) await Promise.resolve().then(() => { console.log(2) }) console.log(3) } fn() // 1 2 3 4
function fn () { return new Promise((resolve) => { console.log('Promise1') fn1() setTimeout(() => { console.log('Promise2') resolve() console.log('Promise3') }, 0); }) } async function fn1() { var p = Promise.resolve().then(() => { console.log('Promise6 // ') }) await p.then(() => { console.log('Promise7') }) console.log('end') } console.log('script') setTimeout(() => { console.log('setTimeout') }, 0) fn().then(() => { console.log('Promise4') }) // script // Promise1 // Promise6 // Promise7 // end // Promise2 //Promise3 // Promise3
class与构造函数的区别?
1 class通过new来实例化对象,构造函数通过调用构造函数返回一个新对象
2 class通过extend实现继承,构造函数通过原型链和原型继承实现继承
3 class类可以使用static关键字定义静态方法,而构造函数不提供直接定义静态方法的功能