引用类型 [重温JavaScript基础(四)]

这篇比较水,主要重温把之前一些疑惑的问题做了补充总结

引用类型综述

引用类型的值(即对象)是引用类型的一个实例。在js中,引用类型是一种数据结构,用于将数据和功能组织在一起。这种形式也常被成为,但这种说法并不妥当。尽管js从技术上讲是一门面向对象的语言,但它不具备传统的面向对象的语言所支持的类和接口等基本结构(es6开始有支持)。引用类型有时候也被称为对象定义,因为它们描述的是一类对象所具有的属性和方法

如前所述,对象是某个特定引用类型的实例。新对象是使用new 操作符后跟一个构造函数来创建的。构造函数本身就是一个函数,只不过该函数是出于创建新对象的目的而定义的。例如:

var person = new Object()

这行代码创建了object引用类型的一个实例,然后把该实例保存在了变量person中。使用的构造函数是Object,它只为新对象定义了默认的属性和方法。js中提供了很多原生引用类型(例如Object),以便开发人员用于实现常见的计算任务。

object类型

object是我们使用最多的一个类型,虽然object类型不具备多少功能,但是对于在应用程序中储存和传输数据而言,这是非常理想的选择。

创建object实例的方式

new Object()

var person = new Object();
person.name = "Nicholas";
person.age = 29;

对象字面量 {}

var person = {
 name : "Nicholas",
 age : 29
};

在通过对象字面量定义对象时,实际上不会调用 构造函数()

通过对象字面量与通过构造函数创建对象的差别:

Object.create()

Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。
可以理解为继承一个对象,es6提供了这个api在语义化上很有帮助,避免了去直接操作__proto__或者prototype。

详情查看MDN Object.create()

const person = {
  isHuman: false,
  printIntroduction: function () {
    console.log(`My name is ${this.name}. Am I human? ${this.isHuman}`);
  }
};

const me = Object.create(person);

me.name = "Matthew"; // "name" is a property set on "me", but not on "person"
me.isHuman = true; // inherited properties can be overwritten

me.printIntroduction();
// expected output: "My name is Matthew. Am I human? true"

Polyfill中的实现:

if (typeof Object.create !== "function") {
    Object.create = function (proto, propertiesObject) {
        if (typeof proto !== 'object' && typeof proto !== 'function') {
            throw new TypeError('Object prototype may only be an Object: ' + proto);
        } else if (proto === null) {
            throw new Error("This browser's implementation of Object.create is a shim and doesn't support 'null' as the first argument.");
        }

        if (typeof propertiesObject != 'undefined') throw new Error("This browser's implementation of Object.create is a shim and doesn't support a second argument.");

        function F() {}
        F.prototype = proto;

        return new F();
    };
}

主要看这三句:

  function F() {}
  F.prototype = proto;//proto是Object.create的第一个参数
  return new F();

上面三句其实等同于

  const _o = {}
  _o.__proto__ = proto
  return _o

验证:

var a={aa:"1"}
var b= Object.create(a)
console.log(b.__proto__==a) //true  

但是因为ie10等老浏览器没有__proto__所以Polyfill使用返回函数实例的方式来写

可见Object.create的作用主要是返回一个对象,这个对象的原型对象是Object.create的第一个参数

Array类型

js中的数组与其他语言最大的差别是数组的每一项可以保存任何类型的数据

这里主要介绍一下Array.prototype的几个方法

栈方法

LIFO(Last-In-First-Out,后进先出)

Array.prototype.push()

push()方法可以接收任意数量的参数,把它们逐个添加到数组末尾,并返回修改后数组的长度

Array.prototype.pop()

pop()方法则从数组末尾移除最后一项,减少数组的 length 值,然后返回移除的项

队列方法

FIFO(First-In-First-Out,先进先出)

Array.prototype.push()

push()方法可以接收任意数量的参数,把它们逐个添加到数组末尾,并返回修改后数组的长度

Array.prototype.shift()

移除数组中的第一个项并返回该项,同时将数组长度减 1。

Array.prototype.unshift()

我管它叫插队方法hhhhhhh
能在数组前端添加任意个项并返回新数组的长度。

操作方法

Array.prototype.concat()

这个方法会先创建当前数组一个副本,然后将接收到的参数添加到这个副本的末尾,最后返回新构建的数组。

位置方法

Array.prototype.concat()

这个方法会先创建当前数组一个副本,然后将接收到的参数添加到这个副本的末尾,最后返回新构建的数组。

Array.prototype.splice()

这个方法会先创建当前数组一个副本,然后将接收到的参数添加到这个副本的末尾,最后返回新构建的数组。

  • 删除:可以删除任意数量的项,只需指定 2 个参数:要删除的第一项的位置要删除的项数。例如,splice(0,2)会删除数组中的前两项。
  • 插入:可以向指定位置插入任意数量的项,只需提供 3 个参数:起始位置0(要删除的项数)和要插入的项。如果要插入多个项,可以再传入第四、第五,以至任意多个项。例如,splice(2,0,"red","green")会从当前数组的位置 2 开始插入字符串"red"和"green"。
  • 替换:可以向指定位置插入任意数量的项,且同时删除任意数量的项,只需指定 3 个参数:起始位置要删除的项数要插入的任意数量的项。插入的项数不必与删除的项数相等。例如,
    splice (2,1,"red","green")会删除当前数组位置 2 的项,然后再从位置 2 开始插入字符串
    "red"和"green"。
var colors = ["red", "green", "blue"];
var removed = colors.splice(0,1); // 删除第一项
alert(colors); // green,blue
alert(removed); // red,返回的数组中只包含一项
removed = colors.splice(1, 0, "yellow", "orange"); // 从位置 1 开始插入两项
alert(colors); // green,yellow,orange,blue
alert(removed); // 返回的是一个空数组
removed = colors.splice(1, 1, "red", "purple"); // 插入两项,删除一项
alert(colors); // green,red,purple,orange,blue
alert(removed); // yellow,返回的数组中只包含一项

迭代方法

这几个迭代方法可能相比直接用for循环并没有效率上的提升,但是对于代码语义性有很大帮助,也能少写几行代码

Array.prototype.every()

every():对数组中的每一项运行给定函数,如果该函数对每一项都返回 true,则返回 true。

Array.prototype.filter()

filter():对数组中的每一项运行给定函数,返回该函数会返回 true 的项组成的数组。

Array.prototype.forEach()

forEach():对数组中的每一项运行给定函数。这个方法没有返回值。

Array.prototype.map()

map():对数组中的每一项运行给定函数,返回每次函数调用的结果组成的数组。

Array.prototype.some()

some():对数组中的每一项运行给定函数,如果该函数对任一项返回 true,则返回 true。

归并方法

Array.prototype.reduce()

都接收两个参数:一个在每一项上调用的函数和(可选的)作为归并基础的初始值。函数接收 4 个参数:前一个值、当前值、项的索引和数组对象。

//求数组中所有值之和
var values = [1,2,3,4,5];
var sum = values.reduce(function(prev, cur, index, array){
 return prev + cur;
});
alert(sum); //15

Date类型

日后整理

RegExp类型

日后整理

Function类型

函数实际上是对象。每个函数都是 Function 类型的实例,而且都与其他引用类型一样具有属性和方法。由于函数是对象,因此函数名实际上也是一个指向函数对象的指针,不会与某个函数绑定。

没有重载

将函数名想象为指针,也有助于理解为什么 js 中没有函数重载的概念。

函数声明和函数表达式

解析器在向执行环境中加载数据时,对函数声明和函数表达式并非一视同仁。解析器会率先读取函数声明,并使其在执行任何代码之前可用(可以访问);至于函数表达式,则必须等到解析器执行到它所在的代码行,才会真正被解释执行

//这是可用的 因为函数声明有提升
alert(sum(10,10));
function sum(num1, num2){
 return num1 + num2;
}
//这是错误的,只有变量sum提升了,但是在执行alert的时候,sum的值是undefined
alert(sum(10,10));
var sum = function(num1, num2){
 return num1 + num2;
};

函数内部属性

在函数内部,有两个特殊的对象:arguments 和 this。

arguments

arguments 是一个类数组对象,包含着传入函数中的所有参数。虽然 arguments 的主要用途是保存函数参数,但这个对象还有一个名叫 callee 的属性,该属性是一个指针,指向拥有这个 arguments 对象的函数。

function factorial(num){
 if (num <=1) {
 return 1;
 } else {
 return num * factorial(num-1)
 }
} 

定义阶乘函数一般都要用到递归算法;如上面的代码所示,在函数有名字,而且名字以后也不会变的情况下,这样定义没有问题。但问题是这个函数的执行与函数名 factorial 紧紧耦合在了一起。为了消除这种紧密耦合的现象,可以像下面这样使用 arguments.callee。

function factorial(num){
 if (num <=1) {
 return 1;
 } else {
 return num * arguments.callee(num-1) //callee指向拥有这个arguments 对象的函数
 }
}

this

函数内部的另一个特殊对象是 this,其行为与 Java 和 C#中的 this 大致类似。换句话说,this引用的是函数据以执行的环境对象

关于this 可以看this 的值到底是什么?一次说清楚

函数属性和方法

前面曾经提到过,ECMAScript 中的函数是对象,因此函数也有属性和方法。每个函数都包含两个属性:length 和 prototype。

length

其中,length 属性表示函数希望接收的命名参数的个数,如下面的例子所示。

function sayName(name){
 alert(name);
}
function sum(num1, num2){
 return num1 + num2;
}
function sayHi(){
 alert("hi");
}
alert(sayName.length); //1
alert(sum.length); //2
alert(sayHi.length); //0

prototype

ECMAScript 核心所定义的全部属性中,最耐人寻味的就要数prototype 属性了。对于ECMAScript 中的引用类型而言,prototype 是保存它们所有实例方法的真正所在。换句话说,诸如toString()和 valueOf()等方法实际上都保存在 prototype 名下,只不过是通过各自对象的实例访问罢了。在创建自定义引用类型以及实现继承时,prototype 属性的作用是极为重要的

call apply

参考this 的值到底是什么?一次说清楚---方应杭

基本包装类型

不做记录

单体内置对象

不做记录

原文地址:https://www.cnblogs.com/riwang/p/12404219.html

时间: 2024-11-01 18:51:22

引用类型 [重温JavaScript基础(四)]的相关文章

JavaScript基础四

1.13 Js中的面向对象 1.13.1 创建对象的几种常用方式 1.使用Object或对象字面量创建对象 2.工厂模式创建对象 3.构造函数模式创建对象 4.原型模式创建对象 1.使用Object或对象字面量创建对象 JS中最基本创建对象的方式: var student = new Object(); student.name = "easy"; student.age = "20"; 这样,一个student对象就创建完毕,拥有2个属性name以及age,分别赋

基本类型和引用类型的值 [重温JavaScript基础(一)]

前言: JavaScript 的变量与其他语言的变量有很大区别.JavaScript 变量松散类型的本质,决定了它只是在特定时间用于保存特定值的一个名字而已.由于不存在定义某个变量必须要保存何种数据类型值的规则,变量的值及其数据类型可以在脚本的生命周期内改变.尽管从某种角度看,这可能是一个既有趣又强大,同时又容易出问题的特性,但 JavaScript 变量实际的复杂程度还远不止如此 基本类型和引用类型的值 复制变量值 传递参数 检测类型 基本类型和引用类型的值 ECMAScript 变量可能包含

重温Javascript(四)-函数

函数 函数声明提升,在执行代码之前会先读取函数声明 sayHi(); function sayHi(){ alert("Hi!"); } 递归 arguments.callee是指向正在执行的函数的指针 还可以换种方式达成一样的效果 var factorial = (function f(num){ if (num <= 1){ return 1; } else { return num * f(num-1); } }); 函数执行的作用域链 function compare(v

重温JavaScript基础(一)

JavaScript的类型 与Java\C语言一样,基本类型包含(数字,对象.函数.布尔.字符串),而在js中,由于null和undefined的特殊性,函数也是对象的这种特殊性,故而js的数据类型包含: Number.String.Boolean.Symbol(符号,第六版新增).Object[Function.Array.Date.RegExp.Math].Null.Undefined 关于javaScript的Number对象 js的数字不区分整数和浮点数,所有的数字均用浮点数表示,尤其是

一步步学习javascript基础篇(3):Object、Function等引用类型

我们在<一步步学习javascript基础篇(1):基本概念>中简单的介绍了五种基本数据类型Undefined.Null.Boolean.Number和String.今天我们主要介绍下复杂数据类型(即引用数据类型) Object类型 我们用的最多的引用类型就属object类型了,一般用来存储和传输数据是再好不过的.然,它的两种创建方式我们是否了解呢? 1.通过构造函数来创建 如: var obj = new Object(); 在js中的引用类型有个非常灵活的用法,可以动态的附加属性和赋值.

javascript基础学习(四)

javascript之流程控制语句 学习要点: 表达式语句含义 选择语句:if.if...else.switch 循环语句:while.do...while.for.for...in 跳转语句:break.continue 异常处理语句:throw.try...catch...finally 一.表达式语句 表达式语句通常是赋值语句.函数或方法调用语句等. 二.选择语句 if(条件表达式)语句;  if(条件表达式){语句;}else{语句;}   还有就是if...lese的嵌套 switch

JavaScript基础--DOM对象加强篇(十四)

1.document 对象 定义:document对象代表的整个html文档,因此可以去访问到文档中的各个对象(元素)document重要的函数 1.1 write 向文档输出文本或js代码 1.2 writeln 向文档输出文本或者js代码,与write不一样的地方是,writeln是换行输出. 比如: document.write("hello");document.writeln("ok"); hello ok 但是对浏览器来看,输出效果没有区别. 1.3 g

JavaScript基础(四)

JavaScript基础(四) 冒泡排序 // 准备一个需要进行排序的数组 var arr = [12, 88, 154, 23, 32, 15, 72, 2, 1, 66]; // 根据分析出的规律进行代码实现 // - 外层循环控制轮数:length - 1 for (var i = 0; i < arr.length - 1; i++) { // - 内层循环控制每轮比较次数: // - 设置条件时发现只 - i 不够,少了1,再设置一个 - 1 凑数 for (var j = 0; j

JavaScript基础-前端开发

理解作用域 理解作用域链是Js编程中一个必须要具备的,作用域决定了变量和函数有权力访问哪些数据.在Web浏览器中,全局执行环境是window对象,这也意味着所有的全局变量或者方法都是window对象的属性或方法.当一个函数在被调用的时候都会创建自己的执行环境,而这个函数中所写的代码就开始进入这个函数的执行环境,于是由变量对象构建起了一个作用域链. var wow = '魔兽世界'; var message = function(){ var _wow = '123'; } 在这个例子中全局环境中