站长家园(原代码之家)(www.adminjie.com)网站源码,微信源码,游戏源码,商业源码分享平台。
当前位置:网站首页 技术文章 网络编程 正文

详解JavaScript基础之对象(整理分享)1 、对象的基础2、混合类对象面试题

时间:2021-12-23 [网络编程]作者:fabuyuan 浏览:8 次

本篇文章给大家带来了JavaScript中关于对象的相关知识,JavaScript中的对象也是变量,但是对象包含很多值,希望看完本篇文章后对大家有帮助。

详解JavaScript基础之对象(整理分享)1 、对象的基础2、混合类对象面试题

1 、对象的基础

1.1 类型

JavaScript有六种主要语言类型:

string , number , boolean ,undefined , null , object

基本类型:string,number,boolean,undefined,null;基本类型本身不是对象。

null

但是 null有时会被当成对象,typeof null 会返回object,实际上null是基本类型。原因是不同对象在底层都是表示为二进制,在JavaScript中二进制前三位都为0会被判断为object类型,null表示全 0,所以typeof时会返回object。

特殊对象子类型

数组

数组也是对象的一种类型,具备一些额外的行为。数组的组织方式比一般的对象要复杂。

函数

函数本质上和普通函数一样,只是可以调用,所以可以操作对象一样操作函数。

1.2 内置对象

String
Number
Date
Boolean
Object
Function
Array

1.3 内容

.a 称之为属性访问,[‘a’]称之为操作符访问。

//对象中的属性名始终是字符串
myobj={}

myobj[myobj]='bar'//赋值

myobj['[object object]'] //'bar'

1.4 可计算属性名

es6 增加可计算属性名,可以在文字形式中使用 [] 包裹一个表达式来当作属性名

var perfix = 'foo'

var myobj={
    [perfix + 'bar'] :'hello'
}

myobj['foobar']//hello

1.5 属性描述符

es5开始,所有属性具备了属性描述符,比如可以直接判断属性是否可读可写等。

/*
 * 重要函数:
 * Object.getOwnPropertyDescriptor(..) //获取属性描述符
 * Object.defineProperty(..) //设置属性描述符
 */
 writeble(可读性)
 configurable(可配置性)
 enumerable (可枚举性)

1.6遍历

for in

for in可用来遍历对象的可枚举属性列表(包括[[Prototype]]链),需要手动获取属性值。可以遍历数组及普通的对象

for of

es6新增,可以用来遍历数组的属性值,for of循环首先会向被访问对象请求一个迭代器对象,然后通过调用迭代器对象的next()方法来遍历所有的返回值。
数组有内置的@@iterator,

for of 如何工作?

var arr = [1, 2, 3]
var it = arr[Symbol.iterator]()//迭代器对象
console.log(it.next());//{value: 1, done: false}
console.log(it.next());//{value: 2, done: false}
console.log(it.next());//{value: 3, done: false}
console.log(it.next());//{value: undefined, done: true}

/*
 * 用es6 的Symbol.iterator 来获取对象的迭代器内部属性。
 * @@iterator本身并不是一个迭代器对象,而是一个返回迭代器对象的函数。
 */

对象如何内置@@iterator,遍历属性的值?

因为对象没有内置一个@@iterator,无法自动完成for…of遍历。但是,可以给你任何想遍历的对象定义@@iterator,举例来说:

  var obj={
      a:1,b:2
  }
  
   Object.defineProperty(obj, Symbol.iterator, {
        enumerable: false,
        writable: false,
        configurable: true,
        value: function () {
            var self = this
            var idx = 0
            var ks = Object.keys(self)
            return {
                next: function () {
                    return {
                        value: self[ks[idx++]],
                        done: (idx > ks.length)
                    }
                }
            }
        }
    })
    
    //手动遍历
    var it = obj[Symbol.iterator]()//迭代器对象
    console.log(it.next());//{value: 1, done: false}
    console.log(it.next());//{value: 2, done: false}
    console.log(it.next());//{value: undefined, done: true}

   //for of 遍历
    for (const v of obj) {
        console.log(v);
    }
    //2
    //3

其他数组遍历函数

 /*
 forEach:会遍历所有并忽略返回值
 some:会一直运行到回调函数返回 true(或者"真"值)
 every:会一直运行到回调函数返回 false(或者"假"值)
 map:
 filter:返回满足条件的值
 reduce:
 some和every 和for的break语句类似,会提前终止遍历
 */

2、混合类对象

面试题

1、对象的深拷贝和浅拷贝

相关知识

基本类型和引用类型

上面提过,JavaScript的主要的语言类型有六种:string、number、boolean、null、undefined、object;其中前5种属于基本类型,最后的object属于引用类型。

JavaScript的变量的存储方式:栈(stack)和 堆(heap)

栈:自动分配内存空间,系统自动释放,里面存放的是基本类型的值和引用类型的地址

堆:动态分配的内存,大小不定,也不会自动释放。里面存放引用类型的值

基本类型与引用类型最大的区别就是 传值 和 传址 的区别

基本类型采用值传递;引用类型采用的地址(指针)传递,将存放在栈内存中的地址赋值给接收的 变量。

浅拷贝和深拷贝是什么?

浅拷贝:对象的浅拷贝,会对‘主’对象进行拷贝,但不会复制对象里面的对象。‘里面的对象’会在原来的对象和它的副本之间共享。

深拷贝:对对象的深拷贝,不仅将原对象的各个属性逐个复制出去,而且将原对象各个属性所包含的对象也依次采用深复制的方法递归复制到新对象上,所以对一个对象的修改并不会影响另一个对象。

举例来说:

var anotherObject={
    b:"b"
}

var anotherArray=[]

var myObject={
    a:'a',
    b:anotherObject, //引用,不是副本
    c:anotherArray //另外一个引用
}

anotherArray.push(anotherObject,myObject)

/*
  如何准确的复制 myObject?
 浅复制 myObject,就是复制出 新对象中的 a 的值会复制出对象中a 的值,也就是 'a',
 但是对象中的 b、c两个属性其实只是三个引用,新对象的b、c属性和旧对象的是一样的。
 
 深复制 myObject,除了复制 myObject 以外还会复制 anotherObject 和 anotherArray。
 但是这里深复制 myObject会出现一个问题,anotherArray 引用 anotherObject 和 myObject,
 所以又需要复制 myObject,这样就会由于循环引用导致死循环。
 后面会介绍如何处理这种情况。
*/

如何实现浅拷贝

object

object.assign()、扩展运算符(…)

var obj1 = {x: 1, y: 2}
var obj2 = Object.assign({}, obj1);
console.log(obj1) //{x: 1, y: 2}
console.log(obj2) //{x: 1, y: 2}
obj2.x = 2; //修改obj2.x
console.log(obj1) //{x: 1, y: 2}
console.log(obj2) //{x: 2, y: 2}

var obj1 = {
    x: 1, 
    y: {
        m: 1
    }
};
var obj2 = Object.assign({}, obj1);
console.log(obj1) //{x: 1, y: {m: 1}}
console.log(obj2) //{x: 1, y: {m: 1}}
obj2.y.m = 2; //修改obj2.y.m
console.log(obj1) //{x: 1, y: {m: 2}}
console.log(obj2) //{x: 2, y: {m: 2}}

Array

slice()、concat、Array.from()、扩展运算符(…)、concat、for循环

var arr1 = [1, 2, [3, 4]], arr2 = arr1.slice();
console.log(arr1); //[1, 2, [3, 4]]
console.log(arr2); //[1, 2, [3, 4]]

arr2[0] = 2 
arr2[2][1] = 5; 
console.log(arr1); //[1, 2, [3, 5]]
console.log(arr2); //[2, 2, [3, 5]]

如何实现深拷贝

JSON.parse(JSON.stringify(obj))

进行JSON.stringify()序列化的过程中,undefined、任意的函数以及 symbol 值,在序列化过程中会被忽略(出现在非数组对象的属性值中时)或者被转换成 null(出现在数组中时)。

var obj1 = {
    x: 1, 
    y: {
        m: 1
    },
    a:undefined,
    b:function(a,b){
      return a+b
    },
    c:Symbol("foo")
};
var obj2 = JSON.parse(JSON.stringify(obj1));

console.log(obj1) //{x: 1, y: {m: 1}, a: undefined, b: ƒ, c: Symbol(foo)}
console.log(obj2) //{x: 1, y: {m: 1}}
obj2.y.m = 2; //修改obj2.y.m
console.log(obj1) //{x: 1, y: {m: 1}, a: undefined, b: ƒ, c: Symbol(foo)}
console.log(obj2) //{x: 2, y: {m: 2}}

深拷贝函数的简单实现

function deepClone(obj){
  let result = Array.isArray(obj)?[]:{};
  if(obj && typeof obj === "object"){
    for(let key in obj){
      if(obj.hasOwnProperty(key)){
        if(obj[key] && typeof obj[key] === "object"){
          result[key] = deepClone(obj[key]);
        }else{
          result[key] = obj[key];
        }
      }
    }
  }
  return result;
}

var obj1 = {
    x: {
        m: 1
    },
    y: undefined,
    z: function add(z1, z2) {
        return z1 + z2
    },
    a: Symbol("foo"),
    b: [1,2,3,4,5],
    c: null
};
var obj2 = deepClone(obj1);
obj2.x.m = 2;
obj2.b[0] = 2;
console.log(obj1);
console.log(obj2);

//obj1
{
a: Symbol(foo)
b: (5) [1, 2, 3, 4, 5]
c: null
x: {m: 1}
y: undefined
z: ƒ add(z1, z2)
}

//obj2
{
a: Symbol(foo)
b: (5) [2, 2, 3, 4, 5]
c: null
x: {m: 2}
y: undefined
z: ƒ add(z1, z2)
}

上面的深拷贝方法遇到循环引用,会陷入一个循环的递归的过程,从而导致爆栈。因此需要改进。

深拷贝函数的改进(防止循环递归)

解决因循环递归而暴栈的问题,只需要判断一个对象的字段是否引用了这个对象或这个对象的任意父级即可。

function deepClone(obj, parent = null){ // 改进(1)
  let result = Array.isArray(obj)?[]:{};
  let _parent = parent;  // 改进(2)
  while(_parent){ // 改进(3)
    if(_parent.originalParent === obj){
      return _parent.currentParent;
    }
    _parent = _parent.parent;
  }
  if(obj && typeof obj === "object"){
    for(let key in obj){
      if(obj.hasOwnProperty(key)){
        if(obj[key] && typeof obj[key] === "object"){
          result[key] = deepClone(obj[key],{ // 改进(4)
            originalParent: obj,
            currentParent: result,
            parent: parent
          });
        }else{
          result[key] = obj[key];
        }
      }
    }
  }
  return result;
}

// 调试用
var obj1 = {
    x: 1, 
    y: 2
};
obj1.z = obj1;
var obj2 = deepClone(obj1);
console.log(obj1); 
console.log(obj2);

深拷贝函数最终版(支持基本的数据类型、原型链、RegExp、Date类型)

function deepClone(obj, parent = null){ 
  let result; // 最后的返回结果

  let _parent = parent; // 防止循环引用
  while(_parent){
    if(_parent.originalParent === obj){
      return _parent.currentParent;
    }
    _parent = _parent.parent;
  }
  
  if(obj && typeof obj === "object"){ // 返回引用数据类型(null已被判断条件排除))
    if(obj instanceof RegExp){ // RegExp类型
      result = new RegExp(obj.source, obj.flags)
    }else if(obj instanceof Date){ // Date类型
      result = new Date(obj.getTime());
    }else{
      if(obj instanceof Array){ // Array类型
        result = []
      }else{ // Object类型,继承原型链
        let proto = Object.getPrototypeOf(obj);
        result = Object.create(proto);
      }
      for(let key in obj){ // Array类型 与 Object类型 的深拷贝
        if(obj.hasOwnProperty(key)){
          if(obj[key] && typeof obj[key] === "object"){
            result[key] = deepClone(obj[key],{ 
              originalParent: obj,
              currentParent: result,
              parent: parent
            });
          }else{
            result[key] = obj[key];
          }
        }
      }
    }
  }else{ // 返回基本数据类型与Function类型,因为Function不需要深拷贝
    return obj
  }
  return result;
}

// 调试用
function construct(){
    this.a = 1,
    this.b = {
        x:2,
        y:3,
        z:[4,5,[6]]
    },
    this.c = [7,8,[9,10]],
    this.d = new Date(),
    this.e = /abc/ig,
    this.f = function(a,b){
        return a+b
    },
    this.g = null,
    this.h = undefined,
    this.i = "hello",
    this.j = Symbol("foo")
}
construct.prototype.str = "I'm prototype"
var obj1 = new construct()
obj1.k = obj1
obj2 = deepClone(obj1)
obj2.b.x = 999
obj2.c[0] = 666
console.log(obj1)
console.log(obj2)
console.log(obj1.str)
console.log(obj2.str)

注:Function类型的深拷贝:

bind():使用fn.bind()可将函数进行深拷贝,但因为this指针指向问题而不能使用;

eval(fn.toString()):只支持箭头函数,普通函数function fn(){}则不适用;

new Function(arg1,arg2,…,function_body):需将参数与函数体提取出来;

PS:一般也不需要深拷贝Function。

【相关推荐:javascript学习教程

以上就是详解JavaScript基础之对象(整理分享)的详细内容,更多请关注站长家园其它相关文章!

本文标签:  JavaScript

转载请注明来源:详解JavaScript基础之对象(整理分享)1 、对象的基础2、混合类对象面试题

本文永久链接地址:https://www.adminjie.com/post/6741.html

免责声明:
本站所发布的一切资源仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容。如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。

附:
二○○二年一月一日《计算机软件保护条例》第十七条规定:为了学习和研究软件内含的设计思想和原理,通过安装、显示、传输或者存储软件等方式使用软件的,可以不经软件著作权人许可,不向其支付报酬!鉴于此,也希望大家按此说明研究软件!

版权声明:
一、本站致力于为软件爱好者提供国内外软件开发技术和软件共享,着力为用户提供优资资源。
二、本站提供的部分源码下载文件为网络共享资源,请于下载后的24小时内删除。如需体验更多乐趣,还请支持正版。
三、我站提供用户下载的所有内容均转自互联网。如有内容侵犯您的版权或其他利益的,若有侵犯你的权益请:提交版权证明文件到邮箱 2225329873#qq.com(#换为@) 站长会进行审查之后,情况属实的会在三个工作日内为您删除。

  • 站长家园(原代码之家)会员升级
  • 最新文章
    • oracle中with的用法是什么

      oracle中with的用法是什么

      在oracle中,with语句可以实现子查询,用于创建一个公共临时表,提高语句执行的效率,语法为“withtempNameas(select....)s...

    • html中改变字体颜色和大小的代码是什么

      html中改变字体颜色和大小的代码是什么

      html中改变字体颜色和大小的代码是“<标签名style="color:颜色值;font-size:字体大小值;">字体内容</标签名>...

    • 记一个ThinkPHP框架的渗透实战

      记一个ThinkPHP框架的渗透实战

      下面thinkphp框架教程栏目将给大家分享一个ThinkPHP框架渗透实战,希望对需要的朋友有所帮助!信息收集找到一个网站http://x.x.x.x/下...

    • mysql锁表的原因是什么

      mysql锁表的原因是什么

      在mysql中,锁表的原因是一个程序执行了对表的insert、update或者delete操作还未commite时,另一个程序也对同一个表进行相同的操作,则此时...

    • css上下文选择器的类型有什么

      css上下文选择器的类型有什么

      css上下文选择器有四种类型:1、后代选择器,可选择当前元素的所有后代元素;2、父子选择器,可选择当前元素的所有子元素;3、同级相邻选择器,可选择拥有共同父级且...

    热门文章