Day3 学习笔记

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

箭头函数和方法扩展

箭头函数和普通函数的区别
  1. 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)
  }
}
  1. 不能作为构造函数 没有prototype属性

    var fn = ()=>{}
    console.log(fn.prototype)  // undefined
  2. 没有arguments对象

var foo = () =>{
	console.log(arguments)
} 
foo(1,"23",true) // 如果是function会输出参数对象,但是箭头函数没有输出
====================
// 这样拿到参数
var foo = (...arguments) =>{
	console.log(arguments)
} 
foo(1,"23",true)
  1. 不能使用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' 
}

特性

  1. async函数内部会返回一个promise对象,如果看起来不是promise,那么它将会隐式的包装在promise中

    async function fn () {
     	return 'hello world' // 调用fn(),返回一个promise对象,状态是已完成,内容是hello world
    // 等价于
      return Promise.resolve('hello world' )
    } 
    console.log(fn())
  2. 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)

  3. await后面promise的状态是reject,则await后的代码不会执行,async函数将返回状态为reject的promise

    async function fn () {
     await Promise.reject('出错了')
       console.log(2)  // 不会执行
    }
    fn()

  4. 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. 验证对象的传值

// 需要实现的功能
// 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
  1. 通过属性查找数组中的特定对象

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的特性
  1. class的数据类型是一个函数

typeof class A {}  function {}
  1. class的原型的constructor指向class

class A {}
A.prototype.constructor === A  // true 
  1. 通过 new 关键字创建出的实例的constructor指向该class

class A {}
var a = new A()
a.constructor === A // true
  1. class内部的方法实际上都是定义在类的prototype上

class A {
  fn () {}
  toString() {}
}

var a = new A()
  1. 通过类创建对象的本质是调用类的constructor,如果类未定义constructor,则会在使用时默认添加

class A {
}
// 等同于
class A {
  constructor() {}
}
  1. class不能直接调用,需要通过new关键字

  2. 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'
    }
}
getset 关键字

etset 关键字用于定义属性的获取器(getter)和设置器(setter)。它们提供了对类属性的访问和修改的控制,并允许在对属性进行读取和赋值时执行自定义的逻辑。

下面是一个例子,演示了在类中使用 getset 的用法:

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关键字定义静态方法,而构造函数不提供直接定义静态方法的功能

  • 39
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值