js 对象的一些知识点

93 阅读3分钟

1.属性、字面量

字面量(literal)是用于表达一个固定值的表示法,又叫常量
通俗的理解,字面就是所见即所得,js 程序执行到代码中的字面量,会立即知道它是什么类型的数据,值是多少
可以用于表示固定值,比如:数字、字符串、undefined、布尔类型的字面值等

属性如下
const person = {
    name: "张三",
    age: 19,
    sex: "男"
}

2.in、for...in

  for...in 
  var mycars = new Array()  
  mycars[0] = "Saab"  
  mycars[1] = "Volvo"  
  mycars[2] = "BMW"  
  for (x in mycars)  
    {  
      console.log(mycars[x] + "<br />")  
   }
  //主要用来进行枚举(如下,对象的循环) 
  
  in 
  判断对象是否为数组/对象的元素/属性:
  格式:(变量 in 对象)......注意,

   当“对象”为数组时,“变量”指的是数组的“索引”;当“对象”为对象是,“变量”指的是对象的“属性”。

  数组示例
  var arr = ["a","b","2","3","str"];  
  var result = ("b" in arr);  
  var result1 = (4 in arr); 
  console.log(result,result1) //true false
  
  对象示例
  var obj={  
    w:"wen",  
    j:"jian",  
    b:"bao"  
  }
  var result=(2 in obj);      
  var result1=("j" in obj);  
  console.log(result)+"<br/>";   //false
  console.log(result1)+"<br/>";  //true
  

3.深拷贝、浅拷贝

 单纯数组与对象的赋值叫做浅拷贝(藕断丝连)
 
 let person = {
    name: "张三",
    age: 19,
    sex: "男"
 }
 
 let newPerson = person;
 newPerson.name = "李四";
 console.log(person);
 console.log(newPerson);
 
 let arr =[1,2,3]
 let newArr = arr
 newArr.push(4)
 console.log(arr, newArr)
 
 解构赋值是深拷贝还是浅拷贝(?)'
 针对一维数组和对象可以看作是深拷贝,多维的是浅拷贝
 let arr =[1,2,3]
 let newArr1 = [...arr]
 newArr1.push(4)
 console.log(arr, newArr1)
 
 let arr2 = [[1,2,3],[4,5,6]]
 let newArr2 = [...arr2]
 newArr2[0].push(888)
 console.log(arr2, newArr2)
 
 深拷贝用法
 let list = [  {id:1,name: '小明',class: "五年级二班"},  {id:2,name: '小红',class: "五年级三班"},  {id:3,name: '小兰',class: "五年级四班"}]

let newList = JSON.parse(JSON.stringify(list)); //深拷贝(常用,简单便捷)=> 80%解决  
newList.push({id: 888})

console.log(list, newList)

//标准的深拷贝 => 引用数据类型(数组、对象)
function deepClone(source){
  // [] => Array(基类) {} => object
  const targetObj = source.constructor === Array ? [] : {}  //外层

  for(let keys in source){
    if(source.hasOwnProperty(keys)){
      //keys => 3 (基本类型、数组、对象)
      if(source[keys] && typeof source[keys] === 'object'){
        //引用数据类型(数组、对象)
        targetObj[keys] = deepClone(source[keys])
      }else {
        //基本数据类型,直接赋值
        targetObj[keys] = source[keys]
      }
    }
  }
  return targetObj
}

4 this 指向问题

console.log(this) // window

function a() {
  console.log(this) //window   全局的方法都会被当作属性挂载到window上,所以指向widown
}


window.a()
//谁调用 => 指向上一个调用者
let o = {
  name: "张三",
  fn: function(){
    console.log(this)
  }
}
o.fn()

let obj = {
  name: "张三",
  fn1: function(){
    console.log(this.name)
  }
}
// window.obj.fn1()

let j = {
  a: 10,
  b: {
    a: 12,
    fn: function(){
      console.log(this.a)
    }
  }
}
j.b.fn()

var id = 66;
function fn2(){
  //箭头函数没有作用域 没有this 它会指向外部
  setTimeout(()=> {
    console.log(this.id)
  },100)
}
// fn2({id: 22})   //怎么指向id为22   call、apply、bind
fn2.call({id:22})

//call、apply、bind 有什么区别 
// call和apply只是传参上有区别  改变this之后还会执行一次函数,  
// apply => []  call => {}
//例如:
var obj = {}//定义一个空的对象
function f(x,y){
    console.log(x,y)
    console.log(this) //this是指obj
}
f.apply(obj,[1,2]) //后面的值需要用[]括起来
f.call(obj,1,2) //直接写

// bind调用只改变this,不会执行函数   

构造函数中,this指向构造函数的实例
普通函数
const Animal = function (name, age){
    this.name = name;
    this.age = age;
}
const animal = new Animal('dog', 3);
console.log(animal); // {name: 'dog', age: 3}

箭头函数
const Animal1 = (name, age) => {
    this.name = name;
    this.age = age;
}
const animal1 = new Animal1('dog', 3);
console.log(animal1); // Uncaught TypeError: Animal is not a constructor

5.new关键字

function Person (){
  this.name = "小明"
  this.fn = function(){
    console.log(`名字是: ${this.name}`)
  }
}

let person1 = new Person(); //继承Person的属性和方法

person1.fn()

// 1.创建一个空对象
let obj = new Object({}) // Object => 基类
// 2.设置它的原型链
obj._proto_ = Person.prototype   //被构造obj的实例的_proto_ 指向 Person构造函数的原型
// 3.改变this指向
let result = Person.call(obj)
// 4.判断返回值类型
if(typeof (result) == "object"){
  person1 = result
}else {
  person1 = obj
}

//构造函数里面 默认 => 新创建的对象
//普通函数里面默认return undefined

// let obj = Object.create(null)  //没有原型链

// let obj2 = {} //有原型链

6.Symbol

Symbol是由ES6规范引入的一项新特性,它的功能类似于一种标识唯一性的ID。通常情况下,我们可以通过调用Symbol()函数来创建一个Symbol实例:

let s1 = Symbol()
let s2 = Symbol('another symbol')
let s3 = Symbol('another symbol')
typeof s1 // 'symbol'
s1 === s2 // false 
s2 === s3 // false

let obj = {
    [Symbol('name')]: '李一两',
    age: 18,
    title: 'Engineer' 
}
Object.keys(obj) // ['age', 'title'] 
for (let p in obj) { console.log(p) // 分别会输出:'age' 和 'title' }  
Object.getOwnPropertyNames(obj) // ['age', 'title']
由上代码可知,Symbol类型的key是不能通过Object.keys()或者for...in来枚举的,它未被包含在对象自身的属性名集合(property names)之中。所以,利用该特性,我们可以把一些不需要对外操作和访问的属性使用Symbol来定义。

也正因为这样一个特性,当使用JSON.stringify()将对象转换成JSON字符串的时候,Symbol属性也会被排除在输出内容之外,

使用ObjectAPI Object.getOwnPropertySymbols(obj) // [Symbol(name)] 
// 使用新增的反射API Reflect.ownKeys(obj) // [Symbol(name), 'age', 'title']

可以用来定义常量,以避免重复i

注册全局Symbol
let gs1 = Symbol.for('global_symbol_1') //注册一个全局
Symbol let gs2 = Symbol.for('global_symbol_1') //获取全局Symbol 
gs1 === gs2 // true

8.optional chaining '?.'

if (db && db.user && db.user.name)  nameLength = db.user.name.length;

const nameLength =
  (db
    ? (db.user
      ? (db.user.name
        ? db.user.name.length
        : undefined)
      : undefined)
    : undefined);
你当然不想编写这样的代码,因此希望有其他选择。其他一些语言使用了被称为“optional chaining”
(可选链)的功能提供了一种优雅的解决方案。根据最近的规范,“Optinal Chain 是一个或多个属性访问和函数调用的链,其中第一个以令牌`?.`开头”。

  const nameLength = db?.user?.name?.length;
  
 [ https://developer.mozilla.org/zhCN/docs/Web/JavaScript/Reference/Operators/Optional_chaining](url)

9.类型转换Sym.toPrimitive

**`Symbol.toPrimitive`** 是内置的 symbol 属性,其指定了一种接受首选类型并返回对象原始
值的表示的方法。它被所有的[强类型转换制]算法优先调用。

ToPrimitive(input, PreferredType)
input是调用的对象,PreferredType是期望返回的结果类型


// 一个没有提供 Symbol.toPrimitive 属性的对象,参与运算时的输出结果。
const obj1 = {};
console.log(+obj1); // NaN
console.log(`${obj1}`); // "[object Object]"
console.log(obj1 + ""); // "[object Object]"

// 接下面声明一个对象,手动赋予了 Symbol.toPrimitive 属性,再来查看输出结果。
const obj2 = {
  [Symbol.toPrimitive](hint) {
    if (hint === "number") {
      return 10;
    }
    if (hint === "string") {
      return "hello";
    }
    return true;
  },
};
console.log(+obj2); // 10  — hint 参数值是 "number"
console.log(`${obj2}`); // "hello"   — hint 参数值是 "string"
console.log(obj2 + ""); // "true"    — hint 参数值是 "default"

1.    
let a = {
 valueOf() {
 return 0;
  },
  toString() {
  return '1';
  },
 [Symbol.toPrimitive]() {
  return 2;
  }
 }
  1 + a // => 3
 '1' + a // => '12'

垃圾回收机制

-   内存:由可读写单元组成,表示一片可操作空间;
-   管理:人为的去操作一片空间的申请、使用和释放;
-   内存管理:开发者主动申请空间、使用空间、释放空间;
-   管理流程:申请-使用-释放
 
var a=1 只要用到它就不是垃圾


function fn(){
   var b=2
   console.log(b)
}

   
let user = {
  name: "John"
};
   
user=null

性能优化

1.避免使用全局变量
2.减少判断层级
3.减少数据读取次数  对于频繁使用的数据,我们要对数据进行缓存。