js组装知识(待续……)

object.assign()

这个方法回使源对象上的[[Get]]取得属性的值,然后使用目标对象上的[[Set]]设置属性的值
实际上对每个源对象执行的是浅复制,如果多个源对象都有相同的属性,则使用最后一个赋值的值

let obj1 = {
  get a() {
    return 'aaa'
  }
}
let obj2 = {
  set a(val) {
    console.log(val)
  }
}
console.log(obj1.a) //'aaa'

// 对象引用
let obj1 = {};
let obj2 = { a:{} }
Object.assign(obj1, obj2)
console.log( obj1.a===obj2.a ) //true

// 错误处理
let obj1 = {}
let obj2 = {
  a: 1,
  get b() {
    throw new Error();
  },
  c: 3
}
try {
  Object.assign(obj1, obj2)
} catch (e) {

}
console.log(obj1)  //{a:1}
// 因此在抛出错误之前,目标对象上已经完成的修改会继续存在

Object.is()

和===很像,接收两个参数

console.log(+0 === -0) //true
console.log(+0 === 0) //true
console.log(-0 === 0) //true
// 判断NaN
console.log(NaN === NaN)//false
console.log(isNaN(NaN))//true
// 用object.is()轻松解决
console.log(Object.is(NaN, NaN))//true
console.log(Object.is(+0, -0))//false
// 要检查超过两个值,利用递归即可
function deep(x, ...rest) {
  // 传入的值为 1,[1,1,4];  1,[1,4] ;  1,[4]
  return Object.is(x, rest[0]) && (rest.length < 2 || deep(...rest))
}
let a = deep(1, 1, 1, 4)
console.log(a) //false

es6增强的对象语法

//1, 属性名简写
let a = 1;
let obj = {a};
console.log(obj) //{a:1}
// 代码压缩程序会在不同的作用域间保留属性名,以防找不到引用   ------这句话还不理解


//2,可计算属性
// 有了计算属性,就可以在对象字面量中完成动态属性赋值.中括号运算符包围
//的对象属性键告诉运行时将其作为JavaScript表达式而不是字符串来求职
// 例
const a = 'name';
const b = 'sex';
const c = 'age';
let obj = { [a]: '小红', [b]: '女', [c]: 10 }
console.log(obj)  //{name:'小红',sex:'女',age:10}
// 当然[]里也可以是一个复杂的函数


// 3,简写方法名
let obj = {
  // 旧的写法
  oldName: function (a) {
    console.log(a)
  },
  //新的写法
  newName(a) {
    console.log(a)
  }
}

解构赋值

// 解构在内部使用ToObject()把源数据结构转换为对象。这也意味着
//在对象结构的上下文中,原始值会被当成对象,这也意味着
// (根据ToObject()的定义),null和undefined不能被结构,否则会抛出错误。
// 例
let { length } = 'foobar';
let { constructor } = 'foobar'
// // 这里一定要切记 源对象转为对象就能看懂了
console.log(length, constructor) //6,ƒ String() { [native code] }
console.log(constructor === String) //true
//嵌套结构
//解构可以使用嵌套结构,以匹配嵌套的属性:
let obj = {
  name: '小明',
  age: 11,
  job: {
    title: 'web前端'
  }
}
let { job: { title } } = obj
console.log(title) // web前端

// 参数解构---对参数进行解构不会影响arguments对象
let data = {
  b: 2, c: 3
}
function aaa(a, { b, c }, d) {
  console.log(arguments)  // 1,[2,3],4
  console.log(b, c)       // 2,3
}
aaa(1, data, 2)

try catch 语句

如果try中的代码没有出错,则程序正常运行try中的内容后,不会执行catch中的内容,
如果try中的代码一但出错,程序立即跳入catch中去执行代码,那么try中出错代码后的
所有代码就不再执行了.

try {
  ({ name: p, work: c, job: b } = obj)
} catch (e) {
}
console.log(p, c, b)

工厂函数

这种工厂模式虽然可以解决创建多个类似对象的问题,但没有解
决对象标识问题(即新创建的对象是什么类型)

function createPerson(name, age, sex) {
  let o = new Object();
  o.name = name;
  o.age = age;
  o.sex = sex;
  o.say = function () {
    console.log(`大家好,我的名字叫---${name}`)
  }
  return o
}
let p1 = createPerson('小明', 11, '男')
p1.say()
let p2 = createPerson('小红', 10, '女')

构造函数

定义构造函数可以确保实例被标识为特定类型
构造函数也是函数,和普通函数的唯一区别在于调用方式不同

function Person(name, age, sex) {
  this.name = name;
  this.age = age;
  this.sex = sex;
  this.job = 'web';
  this.say = function () {
    console.log(`大家好,我的名字叫---${name}`)
  }
}
const p3 = new Person('小刚', 11, '男');
 // 不传参的情况下,可以不加括号,只要有new操作符,就可以调用对应的构造函数

构造函数和工厂函数的区别
1、没有明显的创建对象
2、属性和方法直接赋值给了this
3、没有return

构造函数需要注意的事项?
首字母大写,有助于和普通函数进行区分。

要创建Person实例,需要使用new操作符,以这种方式调用构造函数会进行如下操作。
1> 在内存中创建一个新对象;
2> 这个新对象内部的[[prototype]]特性被赋值为构造函数的prototype属性。
3> 构造函数内部的this被赋值为这个新对象(即this指向新对象)。
4> 执行构造函数内部的代码(给新对象添加属性)
5> 如果构造函数返回非空对象,则返回该对象;否则,返回刚创建的新对象。 ——————不太明白

原型模式

每个函数都会创建prototype属性,这个属性是一个对象,
包含应该由特定引用类型的实例共享的属性和方法。
好处是 在它上面的属性或方法可以被对象实例共享

console.log(Person.prototype)
Person.prototype.say = function () {
  console.log(`我是原型上的方法${this.name}`)
}
console.log(p4)

高阶函数

符合以下一个条件即可

  • 若A函数,接收到的参数是一个函数,那么A就可以称之为高阶函数。
  • 若A函数,调用的返回值依然是一个函数,那么A就可以称之为高阶函数
    常见的有Promist setTimeout 、arr.map()……

函数的柯里化

通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式
例 :

function aaa(a){
  return function bbb(b){
    return function ccc(c){
      return a+b+c
    }
  }
}
d=aaa(1)(2)(3)
console.log(d)

纯函数

  • 一类特别的函数:只要是同样的输入(实参),必定得到同样的输出(返回)
  • 必须遵守以下一些约束
    • 不得改写数据
    • 不会产生任何副作用,例如网络请求,输入和输出设备
    • 不能调用Date.now()或者Math.random()等不纯的方法

nanoid(现在这放着,懒得开新的了)

  • 安装 yarn add nanoid
  • 使用 import {nanoid} from ‘nanoid’;
  • 语法 nanoid()即可生成随机字符

instanceof

检查实例的原型链中,是否包含指定构造函数的原型

  function Person(name) {
    this.name = name
  }
  const p1 = new Person('小红');
  console.log(p1 instanceof Person) //true
  console.log(p1 instanceof Object) //true
  console.log(Person.prototype instanceof Object) //true

isPrototypeOf()

本质上,会在传入参数的[[prototype]]指向调用它的对象时返回true

  function Person(name) {
    this.name = name
  }
  const p1 = new Person('小红');
  const p2 = new Person('小明');
  console.log(Person.prototype.isPrototypeOf(p1)) //true
  console.log(Person.prototype.isPrototypeOf(p2)) //true

Object.getPrototypeOf(obj)

可以方便的取得一个对象的原型。

  function Person(name) {
    this.name = name
  }
  const p1 = new Person('小红');
  console.log(Object.getPrototypeOf(p1)) //{constructor:f}
  console.log(Object.getPrototypeOf(p1)==Person.prototype) //true
 

Object.setPrototypeOf()

可以向实例的私有属性[[prototype]]写入一个新值,这样就可以重写一个对象的原型继承关系
【注意】这个方法可能会影响代码性能,会涉及所有访问了拿些修改过[[prototype]]的对象的代码

  Object.setPrototypeOf(p1, p2)// p1.__proto__===p2

Object.create()

来创建一个新对象,同时为其指定原型

  let biped = { numLegs: 2 };
  let person = Object.create(biped)
  person.name = 'Matt';
  console.log(person.name);  //'Matt'
  console.log(person.numLegs); //2
  console.log(Object.getPrototypeOf(person) === biped); //true

原型层级

  • 在通过对象访问属性时,会按照这个属性的名称开始搜索,搜索开始对象实例本身。
    如果在这个实例上发现了给定的名称,则返回该对象名称对应的值
  • 如果没有找到属性,则搜索会沿着指针进入原型对象,找到则返回该属性对应的值,
    这也就是原型用于在多个对象实例间共享属性的原理。

【注意】通过实例读取原型对象的值是不可更改的,如果在实例上添加一个与
原型对象同名的属性,则会在实例上创建这个属性,这样会遮住原型对象的属性

delect操作符

可以完全删除实例上的属性

  function Person(name) {
	this.name = name;
  }
  let p1 = new Person('小红');
  Person.prototype.name = '小明';
  console.log(p1.name) //'小红'
  delete p1.name;
  console.log(p1.name) //'小明'

hasOwnProperty()

检测是否为实例属性

  function Person() {

  }
  let p1 = new Person();
  let p2 = new Person();
  Person.prototype.name = '小明';
  p1.name = '小红';
  console.log(p1.hasOwnProperty('name'))  //true
  console.log(p2.hasOwnProperty('name'))   //false

Object.getOwnPropertyDescriptor()

取得原型属性的描述符(只对实例属性有效)

  function Person() {
  }
  let p1 = new Person();
  let p2 = new Person();
  Person.prototype.name = '小明';
  p1.name = '小红';
  console.log(Object.getOwnPropertyDescriptor(p1, 'name'))  
  //{value: "小红", writable: true, enumerable: true, configurable: true}
  console.log(Object.getOwnPropertyDescriptor(p2, 'name'))   //undefined

in操作符

  function Person(name) {

  }
  let p1 = new Person();
  console.log('name' in p1)  //false
  p1.name = '小明'
  console.log('name' in p1)  //true
  let p2 = new Person();
  console.log('name' in p2)  //false
  Person.prototype.name = '小红'
  console.log('name' in p2)  //true
  //结论,无论属性在实例自身还是原型上都会返回true

利用in和hasOwnProperty的特性实现检测属性是否存在于原型上

  function detectionP(obj, k) {
    return !obj.hasOwnProperty(k) && ([k] in obj)
  }
  console.log(detectionP(p2, 'name'))

for-in循环中使用in操作符时,可以通过对象访问且可以被枚举的属性都会返回,
包括实例属性和原型属性

  function Person(name) {
	this.name = name
  }
  Person.prototype.age = 10
  let p1 = new Person('小红');
  Object.defineProperties(p1, {
    "school": {
      value: 'bgs',
      enumerable: true //默认false
    },
    "class": {
      value: '1908'
    }
  })
  console.log(p1) //Person {name: "小红", school: "bgs", class: "1908"}
  for (let k in p1) {
    console.log(k)  // name,school,age
  }

Object.keys()

接收一个对象作为参数,返回包含该对象所有可枚举属性名称的字符串数组

  function Person(name) {
	this.name = name
  }
  let p1 = new Person('小红');
  Object.defineProperties(p1, {
    "school": {
      value: 'bgs',
      enumerable: true //默认false
    },
    "class": {
      value: '1908'
    }
  })
  console.log(Object.keys(p1))  //['name']

Object.getOwnPropertyNames()

只要是实例属性(包含原型),无论是否可以枚举,返回对象所有属性名称的字符串数组

  function Person(name) {
    this.name = name
  }
  let k1 = Symbol('k1')
  let p1 = new Person('小红');
  Object.defineProperties(p1, {
    "school": {
      value: 'bgs',
      enumerable: true //默认false
    },
    "class": {
      value: '1908'
    },
    [k1]: {
      value: 1
    }
  })
  console.log(Object.getOwnPropertyNames(p1))  //["name", "school", "class"]

Object.getOwnPropertySymbols()

只针对符号,类似getOwnPropertyNames
语法Object.getOwnPropertySymbols(obj)

属性枚举的顺序

  • for-in和Object.keys()的枚举顺序是不确定的,取决于javascript引擎,
    可能因浏览器而异
  • Object.getOwnPropertyName()、Object.getOwnPropertySymbols()
    和object.assign()的枚举顺序是确定性的。
  • 先以升序枚举数值键,然后以插入顺序枚举字符串和符号键,
    在对象字面量中定义的键以它们逗号分隔的顺序插入。

ECMAScript2017新增两个静态方法

Object.values()

接收一个对象,返回对象值的数组(浅复制)

Object.entries()

接收一个对象,返回键值对数组(浅复制)。非字符串属性会被转换为字符串输出,
符号属性会被忽略

  let k1 = Symbol('k1')
  let o = {
    a: 1,
    b: 2,
    c() { },
    [k1]: 3
  }
  console.log(Object.values(o))  //[1,2,fn]
  console.log(Object.entries(o))  //[['a',1],['b',2],['c',fn]]

原型的语法

之前每次给原型上增加属性都要通过Person.prototype.key=value


 
  function Person() { }
  //错误的写法1,会丢失constructor属性
  Person.prototype = {
    name: '小红',
    age: 10
  }
  const p1 = new Person()
  console.log(Person.prototype.constructor)  //ƒ Object() { [native code] }

  //错误的写法2,这样会得到一个[[Enumberable]]为true的constructor属性
  Person.prototype = {
    constructor: Person,
    name: '小红',
    age: 10
  }
  const p1 = new Person()
  console.log(p1.constructor) 
   for (let k in p1) {
      console.log(k)   //name,age,constructor
    }
  
  //正确的写法
  Person.prototype = {
    name: '小红',
    age: 10
  }
  Object.defineProperty(Person.prototype, 'constructor', {
    value: Person,
    enumerable: false
  })
  const p1 = new Person()
  for (let k in p1) {
    console.log(k)   //name,age
  }

实例只有指向原型的指针,没有指向构造函数的指针.
重写构造函数上的原型之后在创建的实例才会引用引得原型。而再次之前创建的实例仍然会引用最初的原型

  //例1
  let p1 = new Num()
  function Num() {
  }
  Num.prototype.count = 2
  console.log(p1.count)  //2

  //例2
  let p1 = new Num()
  function Num() {
  }
  Num.prototype = {
    constructor: Num,
    count: 2
  }
  let p2 = new Num()
  console.log(p1.count)  //undefined
  console.log(p2.count)  //2

toString()

方法可把一个 Number 对象转换为一个字符串,并返回结果。
一个参数:可以返回2~32进制的字符串

检查数据类型

Object.prototype.toString.call()

继承

原型链继承的问题

  • 主要问题出现在原型中包含引用值的时候(相同引用)
  • 子类型在实例化时不能给父类型的构造函数传参

盗用构造函数(也叫“经典继承”或“对象伪装”)

使用apply()和call()方法以新创建的对象为上下文执行构造函数
缺点不能访问父类原型上定义的方法

    function Animal(color) {
      this.color = color
    }
    function Dog(red, name) {
      Animal.call(this, red)
      this.name = name
    }
    const d1 = new Dog('红色', '小黄')
    console.log(d1)

组合继承

就是综合了原型链和盗用构造函数,将两者的优点结合。
基本思路就是使用原型链继承原型上的属性和方法,而通过盗用构造函数继承实例属性
调用过程中会执行两次SuperType函数

    function SuperType(name) {
      this.name = name;
      this.colors = ['red', 'blue', 'green'];
    }

    SuperType.prototype.sayName = function () {
      console.log(this.name);
    }

    function SubType(name, age) {
      // 继承属性
      SuperType.call(this, name);
      this.age = age;
    }

    // 继承方法
    SubType.prototype = new SuperType()

    SubType.prototype.sayAge = function () {
      console.log(this.age);
    }

    const instance1 = new SubType('小红', 19)
    instance1.sayAge()
    instance1.sayName()
    console.log(instance1)

寄生式组合继承(效果最佳) (红宝石248页)

只会调用一次SuperType函数

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值