对象的拓展
- 写法太灵活了
const s = {
demo: function(){
}
}
等同于
const s = {
demo(){
}
}
方法的name属性
- 对象的方法 也为函数,具有name属性
- 如果对象内部存在set get 那么其对象的name属性,是附着在其set get上的 获得该属性的描述对象
- enumerable 可枚举属性
- for in 自身的以及继承的可枚举属性
- Object.assign() 拷贝对象自身的可枚举属性
- Object.keys() 自身的可枚举属性键名
- JSON.stringily 串行化自身的可枚举属性
属性遍历的五种方法
- for in 自身的和继承的所有属性(可枚举的)
- Object.keys() 自身的所有属性(可枚举的)
- Reflect.ownKeys() 自身的所有属性
- Object.getOwnPropertyNames(obj) 自身的所有属性(非Symbol)
- Object.getOwnPropertySymbols(obj) 自身的Symbol属性的键名
遍历规则
- 数值 字符 Symbol
super 关键字
this总是指向函数所在的当前对象,super总是指向当前对象的原型对象
- super目前只能在对象的方法中才能生效
const obj = {
foo(){
super.foo
}
}
解构赋值
从一个对象取值。将可遍历的属性但未分配的属性,放置到指定的对象上。键和值都会拷贝在新对象上。
- ... 拓展运算符 结果为对象
- ... 等同于Object.assign
let obj = {
...a
}
===
const obj=Object.assign({},a)
上面的例子只是拷贝了对象实例的属性,如果想完整克隆一个对象,还拷贝对象原型的属性,可以采用下面的写法。
方法一:
const objclone = {
__proto__: Object.getPrototypeOf(obj),
...obj
}
方法二:
const objclone = Object.assign(
Object.create(Object.getPrototypeOf(obj)),
obj
)
方法二函数式写法:
function clone(obj){
let demo = Object.getPrototypeOf(obj)
return Object.assign(demo,obj)
}
方法三:
const clone3 = Object.create(
Object.getPrototypeOf(obj),
Object.getOwnPropertyDescriptors(obj)
)
下方为拓展阅读,说明一下JS的内置对象的使用方法 Object.getOwnPropertyDescriptors Object.getPrototypeOf Object.getOwnPropertyDescriptors 与 Object.defineProperties() 方法合用,可以实现 assign 不能实现的对get set的拷贝
const source = {
set foo(value) {
console.log(value);
}
};
const target2 = {};
Object.defineProperties(target2, Object.getOwnPropertyDescriptors(source));
Object.getOwnPropertyDescriptor(target2, 'foo')
// { get: undefined,
// set: [Function: set foo],
// enumerable: true,
// configurable: true }
函数式写法
const shallowMerge = (target, source) => Object.defineProperties(
target,
Object.getOwnPropertyDescriptors(source)
);
手写
function getOwnPropertyDescriptors(obj) {
const result = {};
for (let key of Reflect.ownKeys(obj)) {
result[key] = Object.getOwnPropertyDescriptor(obj, key);
}
return result;
}
const object1 = {
property1: 42,
get foo() {},
set foo(x) {}
};
const descriptor1 = Object.getOwnPropertyDescriptors(object1, 'object1');
console.log(descriptor1)
Object { property1: Object { value: 42, writable: true, enumerable: true, configurable: true }, foo: Object { get: get foo() {}, set: set foo(x) {}, enumerable: true, configurable: true } }
console.log(Object.getPrototypeOf(object1))
const object1 = {
property1: 42,
get foo() {},
set foo(x) {}
};
console.log(Object.getPrototypeOf(object1))
Object { } // 非浏览器环境下的结果
浏览器结果
{constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}
下方依然为拓展阅读 prototype proto
const object1 = {
property1: 42,
get foo() {},
set foo(x) {}
};
console.log(Object.getPrototypeOf(object1))
console.log(object1.__proto__.Prototype)
console.log(object1.Prototype)
console.log(object1.__proto__)
结果为: Object.getPrototypeOf(object1) === object1.__proto__
我一开始认为是 Object.getPrototypeOf(object1) === object1.prototype
{constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}
undefined
undefined
{constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}
prototype是构造器/函数才具有的属性 JavaScript内置的构造器有以下几个: Object,Number,Boolean,String,Array,RegExp,Date,Function,Error,Math,JSON等, 其中Math和JSON是以对象形式存在的,无需new便可创建。当我们用 var mm=new Math(1234565);的时候,会报错。 Object,Number,Boolean,String,Array,RegExp,Date,Function,Error的原型是Function.prototype。而Math和JSON对象的原型是Object.prototype。
对象的新增方法
- Object.is(a,b) 判断两个值是否相等
==(相等运算符) ===(严格相等运算符)前者会自动转换数据类型,后者的NaN不等于自身 +0 和 -0相等
- Object.assign(target,source1,source2) 是将目标对象的可枚举属性复制到源对象上
- 只有字符串的包装对象,才会产生可枚举属性 注意点
- 浅拷贝
- 同名属性的替换
- 数组的处理 将数组当成对象来处理
- 取值函数的处理 只能进行值的复制,如果为取值函数。那么取值后进行处理
proto 属性
- 其本质上不是一个对外暴漏的API
- 目前推荐使用Object.setPrototypeOf(),Object.getPrototypeOf(),Object.create()
- proto 调用的是Object.prorotype.proto
- Object.setPrototypeOf(obj,proto)
function Rectangle() {
// ...
}
const rec = new Rectangle();
Object.getPrototypeOf(rec) === Rectangle.prototype
// true
源码实现
Object.defineProperty(Object.prototype, '__proto__', {
get() {
let _thisObj = Object(this);
return Object.getPrototypeOf(_thisObj);
},
set(proto) {
if (this === undefined || this === null) {
throw new TypeError();
}
if (!isObject(this)) {
return undefined;
}
if (!isObject(proto)) {
return undefined;
}
let status = Reflect.setPrototypeOf(this, proto);
if (!status) {
throw new TypeError();
}
},
});
function isObject(value) {
return Object(value) === value;
}
JS中创建对象的N种方式
- 创建一个Object的对象实例而后为其添加属性和方法
var person = new Object();
person.name = "zhang"
person.sayname = function(){
alert(this.name)
}
- 对象字面量
var person = {
name: "zhang"
sayname: function(){
}
}
- 工厂函数
function createperson(name,age,job){
var o = new Object();
o.name = name
o.sayname = function(){
}
return o
}
var person1 = createperson("zhang",29,"engineer")
console.log(person1 instanceof createperson) false
console.log(person1 instanceof Object) true
- 构造函数
function Person(name,age,job){
this.name = name
this.sayname = function(){
}
等同于
this.sayname = new Function()
}
var person1 = new Person("zhang",29,"engineer")
console.log(person1 instanceof Person) true
console.log(person1 instanceof Object) true
new,所经历的四个过程
- 创建一个新的对象
- 将构造函数的作用域赋给新对象
- 执行构造函数代码
- 返回新对象
构造函数模式中person1.constructor === person
- 原型创建对象模式
function Person(){
}
Person.prototype.name = 'Nike'
Person.prototype.sayName = function(){
}
var person1 = new Person();
6.组合使用构造函数模式和原型模式
function Person(name,age,job){
this.name =name;
this.age = age;
this.job = job;
}
Person.prototype = {
constructor:Person,
sayName: function(){
alert(this.name);
};
}
var person1 = new Person('Nike',20,'teacher');
xdm,学费了
运算符的拓展
没啥可以记的
Symbol
感觉也没啥可以记的
Set 和 Map 数据结构
- Set 集合
- Map Hash表 与对象类似
- 不同的是 Object 结构提供了 字符串-值得对应 Map结构提供了值-值得对应
具体有需要再看吧,不是应用得重点
Proxy 拦截
- 所有从外部的访问,首先需要通过这层拦截。从而提供了一种机制,可以对外界的访问过滤以及改写
Reflect 多数是和Proxy配合食用,后面会说
var proxy = new Proxy(target, handler);
- proxy接受两个参数,target 为所代理的目标对象,也就是没有Proxy介入时,操作原本要访问的对象
- 第二参数就是处理函数
get
get(target,key,receiver) 目标对象 属性名 本身 理解一下get的第三个参数 指向proxy实例本身
const proxy = new Proxy({},{
get: function(target,key,receiver){
return receiver
}
})
console.log(proxy.a == proxy)
set
set(obj,prop,value,receiver) 目标对象,属性名 ,属性值,本身
apply
...arguments 其中的一个关键的用法就是 传递参数
var twice = {
apply (target, ctx, args) {
console.log(...arguments[2])
return Reflect.apply(...arguments) * 2;
}
};
function sum (left, right) {
return left + right;
};
var proxy = new Proxy(sum, twice);
proxy(1, 2) // 6
proxy.call(null, 5, 6) // 22
proxy.apply(null, [7, 8]) // 30
construct
(如下)参数:目标对象,构造函数的参数数组 ,new的那个原对象
const handler = {
construct (target, args, newTarget) {
return new target(...args);
}
};
Reflect
- 将部分语言内部的属性放置在Reflect上
- 与Proxy 一起食用
观察者模式(Observer mode)指的是函数自动观察数据对象,一旦对象有变化,函数就会自动执行。 代码示例
const queueobservable = new Set()
const observe = function(fn){
return queueobservable.add(fn)
}
const observable = (obj) => new Proxy(obj, {set})
function set(target,key,value,receiver){
const result = Reflect.set(target,key,value,receiver)
queueobservable.forEach(observe => observe())
return result
}
const person = observable({
name: '张三',
age: 20
});
function print() {
console.log(`${person.name}, ${person.age}`)
}
observe(print);
person.name = '李四';
// 输出
// 李四, 20
Promise
一种异步处理的方法
现在应该不怎么用了,用的async awiat 多一点。但面试常考
先上例子,方便理解
const promise = new Promise(function(resolve,reject){
if(`success`){
resolve(value)
}else{
reject(error)
}
})
promise.then(function(value){
// success
},function(error){
// fail
});
同时需要注意的是Promise函数执行的顺序
let promise = new Promise(function(resolve, reject) {
console.log('Promise');
resolve();
});
promise.then(function() {
console.log('resolved.');
});
console.log('Hi!');
// Promise
// Hi!
// resolved
promise函数遇到则立即执行,而后等待同步代码执行完后,再执行回调函数
示例,说明函数执行的顺序以及规则
new Promise((resolve, reject) => {
resolve(1);
console.log(2);
}).then(r => {
console.log(r);
});
// 2
// 1
new Promise((resolve, reject) => {
return resolve(1);
console.log(2);
}).then(r => {
console.log(r);
});
// 1
达咩,学不动了,呜呜呜~~,有没有前端的小伙伴交流一下学习心得
Iterator 遍历器
- 提供一个统一方便的接口,为for of 服务,使得该数据结构能够按照某种次序排序
- 遍历器对象本质上为指针,调用next方法。向后延展
四种数据结构 Map Set Array Object
- Symbol.iterator属性,就可以认为是“可遍历的”
- 原生具备 Iterator 接口的数据结构如下。
- Array
- Map
- Set
- String
- TypedArray
- 函数的 arguments 对象
- NodeList 对象
let arr = ['a', 'b', 'c'];
let iter = arr[Symbol.iterator]();
iter.next() // { value: 'a', done: false }
iter.next() // { value: 'b', done: false }
iter.next() // { value: 'c', done: false }
iter.next() // { value: undefined, done: true }
类似数组对象,布置Iterator接口,使其可以遍历
let iterable = {
0: 'a',
1: 'b',
2: 'c',
length: 3,
[Symbol.iterator]: Array.prototype[Symbol.iterator]
};
for (let item of iterable) {
console.log(item); // 'a', 'b', 'c'
}
for of 搭配 Iterator 没有的话 for in
不同于forEach方法,它可以与break、continue和return配合使用。
总之,for...in循环主要是为遍历对象而设计的,不适用于遍历数组。
Generator 函数的语法
Generator函数是一种状态机函数,还是一个遍历器对象生成函数。返回的遍历器对象可以依次状态机的每一个状态
Generator的相关应用
js为传值调用
Thunk 函数自动执行Generator函数的一种方法 此方法为传名调用
传值调用与传名调用,各有利弊。传值调用比较简单。相当于传入参数前计算其数值。但可能会有一些性能的损失
Thunk函数可以实现Generator的函数的自动执行。因为Generator函数本身是通过调用next方法执行的。通过这种方法将异步的事件化为同步进行执行。 自动控制 Generator 函数的流程,接收和交还程序的执行权
手搓run自动执行Generator函数
function run(fn) {
var gen = fn();
function next(err, data) {
var result = gen.next(data);
if (result.done) return;
console.log(result.value)
next()
}
next();
}
var g = function* (){
var f1 = yield 1;
var f2 = yield 2;
// ...
var fn = yield 3;
};
run(g)
控制台结果:
1
2
3
async
上面讲到的很多东西实际上都是对于异步过程的不断修正。因为JS是一种单线程的脚本语言。
- 内置执行器 async从外部看跟普通函数完全一致
- 更好的语义
- 更好的适应性
- 返回值为Promise 比Iterator对象稍微好一点
通过代码介绍用法
async function getStockPriceByName(name) {
const symbol = await getStockSymbol(name);
const stockPrice = await getStockPrice(symbol);
return stockPrice;
}
getStockPriceByName('goog').then(function (result) {
console.log(result);
});
简易的函数休眠的写法
function timeout(ms){
return new Promise((resolve) => {
setTimeout(resolve,ms)
})
}
async function asyncPoint(value , ms){
await timeout(ms)
console.log(value)
}
asyncPoint('helloworld',50)
// async函数的花式写法
// 函数声明
async function foo() {}
// 函数表达式
const foo = async function(){}
// 箭头函数
const foo = async (()=>{
})
// async 实现动画的触发,若出现问题则返回上一个动画的返回值
async function chainAnimationsAsync(elem, animations) {
let ret = null;
try {
for(let anim of animations) {
ret = await anim(elem);
}
} catch(e) {
/* 忽略错误,继续执行 */
}
return ret;
}
获取url并按照顺序打印
// fetch 函数为获取url 继发获取url并按照顺序打印
async function readUrl(urls){
for (const url of urls){
const response = await fetch(url)
console.log(await response.text())
}
}
// 并发获取url并按照顺序打印
async function readUrl(urls){
const texts = urls.map(async url =>{
const response = await fetch(url)
return await response.text()
})
for (const text of texts){
console.log(await text)
}
}
class
本质上来讲 ES6的类只是ES5构造函数的一种包装。很多的属性都是继承于此
// 代码中的bind相当于讲this指向指向内部
class Logger {
constructor() {
this.printName = this.printName.bind(this);
console.log(this.printName)
}
printName(name = 'there') {
this.print(`Hello ${name}`);
}
print(text) {
console.log(text);
}
}
const logger = new Logger();
const { printName } = logger
console.log(printName())
箭头函数内部的this总是指向定义时所在的对象
class Obj {
constructor() {
this.getThis = () => this;
}
}
const myObj = new Obj();
myObj.getThis() === myObj // true
class 可以通过extends关键字实现继承。相比较之前原型链的方法。better
super()在这里相当于Obj.prototype.constructor.call(this)。
其中需要说明的是将右边的this注入到左边的Obj方法当中,使用call,apply可以劫持别人的想法。(属性和方法),从而实现继承
class Obj{}
class obj extends Obj{
constructor(){
super()
}
}
定义在实例上的方法以及属性是无法通过super获取的
class A {
constructor() {
this.p = 2;
}
}
class B extends A {
get m() {
return super.p;
}
}
let b = new B();
b.m // undefined
如果属性定义在父类的原型对象上,super就可以取到。
这一点可以推敲一下
class A {}
A.prototype.x = 2;
class B extends A {
constructor() {
super();
console.log(super.x) // 2
}
}
let b = new B();
- 子类必须在constructor方法中调用super方法,因为子类通过父类的构造函数完成构造,获得与父类相同的属性和方法,而后通过添加自身的属性和方法。完成构造。不用super,就得不到this对象。
- 调用super之前,无法使用this
- 在子类方法中调用父类方法时,方法内部的this指向子类的方法实例
super.x 在赋值过程中相当于this.x. 而在console.log(super.x) 则相当于A.prototypeof.x
class A {
constructor() {
this.x = 1;
}
}
class B extends A {
constructor() {
super();
this.x = 2;
super.x = 3;
console.log(super.x); // undefined
console.log(this.x); // 3
}
}
let b = new B();
proto 与 prototype
class中的链之间的关系
- 实例的__proto__总是指向父类,表示构造函数的继承
- 而B的prototype属性,则表示方法的继承,B.prototype.proto === A.prototype // true。
作为一个对象,子类(B)的原型(__proto__属性)是父类(A);作为一个构造函数,子类(B)的原型对象(prototype属性)是父类的原型对象(prototype属性)的实例
class A {
}
class B extends A {
}
B.__proto__ === A // true
B.prototype.__proto__ === A.prototype // true
prototype 意思就是实例
- 子类的原型的原型就是父类的原型
原生构造函数的继承
原生构造函数是指语言内置的构造函数,通常用来生成数据结构
- Boolean() Number() String() Array() Date() Function() RegExp() Error() Object()
此方法完全不生效
function MyArray() {
Array.apply(this, arguments);
}
MyArray.prototype = Object.create(Array.prototype, {
constructor: {
value: MyArray,
writable: true,
configurable: true,
enumerable: true
}
});
const array = new MyArray()
array[0] = 'red'
array.length // 0
子类无法获得原生构造函数的内部属性,故会出错
class MyArray extends Array{
constructor(...args){
super(...args)
}
}
const array = new MyArray()
array[0] = 1
console.log(array.length) // 1
- 所以extends不仅仅可以继承类,还可以继承原生的构造函数
- 由此你可以构建自身的数据结构
Mixin模式的实现
- Mixin 指多个对象合并为一个对象。新对象具有各个组成成员的接口
call,apply,bind
- 用于改变this的指向。
- 传递参数的区别 call,bind 参数使用,隔开. apply 则为数组对象
- bind 返回的是一个新的函数 后面加() 才能触发调用
2021-9-14 感觉有的笔记被吞了,我有没有证据 以后写完都要带上日期