一:原型链
原型链是 JavaScript 中实现对象继承的一种机制。它基于原型的概念,通过将对象的属性和方法定义在原型对象上,实现了对象之间的属性和方法的共享和继承。
在 JavaScript 中,每个对象都有一个原型对象(prototype)。原型对象可以是另一个对象,也可以是 null。当我们访问一个对象的属性或方法时,如果对象本身没有定义该属性或方法,JavaScript 引擎会沿着原型链向上查找,直到找到该属性或方法或者到达原型链的顶端(即原型对象为 null)
每一个对象都从原型继承属性,直到null结束。
1、构造函数、原型和实例的关系
a,构造函数都有一个属性prototype,这个属性是一个对象,是Object的实例;
b,原型对象prototype里有一个constructor属性,该属性指向原型对象所属的构造函数;
c,实例对象都有一个__proto__属性,该属性指向构造函数的原型对象;
obj.__proto__===Object.prototype
2、prototype与_proto_的关系
a,prototype是构造函数的属性;
b,__proto__是实例对象的属性;
c,两者都指向同一个对象;
原型链作为继承的基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。
每个构造函数都有一个原型对象prototype,原型对象都包含一个指向构造函数的指针constructor,而实例都包含一个指向原型对象的内部指针 [[prototype]]。
如果让原型对象等于另一个类型的实例,那么原型对象将包含一个指向另一个原型的指针,相应地,另一个原型中也包含着一个指向另一个构造函数的指针。
通过原型链,我们可以实现对象之间的属性和方法的共享和继承,提高代码的复用性和效率。
需要注意的是,原型链是一个单向的链式结构,从下往上查找属性和方法。如果在原型链上的某个对象上找到了属性或方法,就会停止查找,不会继续向上查找。
数组直接量(语法糖)
var arr = [10, 20]
对象直接量(语法糖)
var obj = { age: 1 }
函数直接量(语法糖)
function fn() { }
实例练习:
var F = function() {}
Object.prototype.a = function() {
console.log('a()')
}
Function.prototype.b = function() {
console.log('b()')
}
var f = new F()
f.a()
f.b()
F.a()
F.b()
f是构造函数F的实例对象,因此有f.__proto__ === F.prototype
F.prototype也是一个普通对象,可以理解为Object构造函数创建的,因此有F.prototype.__proto__ === Object.prototype
F作为函数对象,也有自己的__proto__,指向它的原型Function.prototype
Function.prototype也是一个普通对象,可以理解为Object构造函数创建的,因此有Function.prototype.__proto__ === Object.prototype
总结:两条原型链
对于f,有以下
console.log(f.__proto__ === F.prototype) //true
console.log(F.prototype.__proto__ === Object.prototype) //true
对于F,有以下
console.log(F.__proto__ === Function.prototype) //true
console.log(Function.prototype.__proto__ === Object.prototype) //true
所以题目中函数b没有出现在f的原型链中,所以答案:
f.a() // a()
f.b() // b is not a function(报错)
F.a() // a()
F.b() // b()
二:引用数据和基本数据
在 JavaScript 中,数据类型可以分为两类:基本数据类型和引用数据类型。
基本数据类型(Primitive Data Types)包括:
- 数字(Number):表示数值,例如
1
、3.14
。 - 字符串(String):表示文本,例如
"Hello"
、'World'
。 - 布尔值(Boolean):表示真或假,只有两个值:
true
和false
。 - null:表示一个空值。
- undefined:表示一个未定义的值。
- Symbol:表示唯一的标识符,ES6 新增的数据类型。
引用数据类型(Reference Data Types)包括:
- 对象(Object):表示复杂的数据结构,可以包含多个属性和方法。例如,
{}
表示一个空对象,{ name: "Alice", age: 25 }
表示一个包含name
和age
属性的对象。 - 数组(Array):表示一组有序的数据,可以通过索引访问每个元素。例如,
[1, 2, 3]
表示一个包含三个元素的数组。 - 函数(Function):表示可执行的代码块,可以接受参数并返回值。例如,
function add(a, b) { return a + b; }
表示一个加法函数。
基本数据类型是按值访问的,每个变量都包含自己的值,它们在内存中占据固定的空间。当将一个基本数据类型的值赋给另一个变量时,会创建一个新的值的副本。
引用数据类型是按引用访问的,变量存储的是对象的引用(内存地址),而不是对象本身。多个变量可以引用同一个对象,它们共享同一个对象的数据。当将一个引用数据类型赋给另一个变量时,只是复制了对象的引用,而不是对象本身。
需要注意的是,虽然 null
和 undefined
都表示空值,但它们的含义略有不同。null
表示一个空对象指针,而 undefined
表示一个未定义的值。
基本数据 也称为原始数据 是最小单位 不能分割
基本数据在使用点语法时 就会隐式操作 利用构造函数创建一个对象 然后用这个创建的对象去调用这个点语法
实例练习:
Array.prototype.x = "你好,vscode"
var mypush = Array.prototype.push
Array.prototype.push = function (arg) {
mypush.call(this, arg)
console.log("你好,vscode")
}
var arr1 = new Array(10, 20, 30)
var arr2 = [10, 20, 30]
arr1.push(1100)
arr2.push(1100)
console.log(arr1.x, arr2.x)
console.log(arr1.push, arr2.push)
//系统内置的构造函数不能修改原型对象
运行结果:
你好,vscode
你好,vscode
你好,vscode 你好,vscode
ƒ (arg) {
mypush.call(this, arg)
console.log("你好,vscode")
} ƒ (arg) {
mypush.call(this, arg)
console.log("你好,vscode")
}
//连等 会一开始就访问变量的内存空间
var x1 = {}
var x2 = x1
x1.a = 200
x1 = { a: 200 }
console.log(x2.a) //200
三:构造函数construct
构造函数(Constructor)是一种特殊的函数,用于创建和初始化对象。在 JavaScript 中,我们可以使用构造函数来定义自定义的对象类型,并通过 new
关键字来创建该类型的实例对象。
构造函数的命名通常以大写字母开头,以便与普通函数区分开来。构造函数内部使用 this
关键字来引用新创建的对象,并通过给 this
添加属性和方法来初始化对象。
实例练习:
function Person(name, age) {
this.name = name;
this.age = age;
this.sayHello = function() {
console.log("Hello, my name is " + this.name + " and I am " + this.age + " years old.");
};
}
var person1 = new Person("Alice", 25);
person1.sayHello(); // 输出 "Hello, my name is Alice and I am 25 years old."
var person2 = new Person("Bob", 30);
person2.sayHello(); // 输出 "Hello, my name is Bob and I am 30 years old."
四:内置对象——数组
数组的头尾操作
方法 | 功能 |
---|---|
push() | 在尾部插入新项 |
pop() | 在尾部删除 |
unshift() | 在头部插入新项 |
shift() | 在头部删除 |
push() 方法
push()
方法用来在数组末尾推入新项,参数就是要推入的项。
如果要推入多项,可以用逗号隔开。
调用 push()
方法后,数组会立即改变,不需要赋值。
var arr = [22, 33, 44, 55];
arr.push(66);
arr.push(77, 88, 99);
console.log(arr);
// [22, 33, 44, 55, 66, 77, 88, 99]
pop() 方法
与 push() 方法相反,pop()
方法用来删除数组中的最后一项。
()
里没有参数,默认弹出最后一项。
pop()
默认返回最后一项的值。
var arr = [22, 33, 44, 55];
var item = arr.pop();
console.log(arr); // [22, 33, 44]
console.log(item); // 55
unshift() 方法
unshift()
方法用来在数组头部插入新项,参数就是要插入的项。
如果要插入多项,可以用逗号隔开。
调用 unshift()
方法后,数组会立即改变,不需要赋值
shift() 方法
与 unshift() 方法相反,shift()
方法用来删除数组中的开头一项。
()
里没有参数,默认弹出开头一项。
shift()
默认返回开头一项的值。
splice() 方法
splice()
方法用于替换数组中的指定项。
- 替换项
var arr = ['A', 'B', 'C', 'D', 'E', 'F', 'G'];
// 从下标为 3 的项开始,连续替换 2 项。即将 'D', 'E' 替换为 'X', 'Y', 'Z'
arr.splice(3, 2, 'X', 'Y', 'Z');
console.log(arr);
// ['A', 'B', 'C', 'X', 'Y', 'Z', 'F', 'G']
- 插入项
var arr = ['A', 'B', 'C', 'D'];
// 从下标为 2 的项开始,连续替换 0 项,即:在 [2] 处插入。
arr.splice(2, 0, 'X', 'Y', 'Z');
console.log(arr);
// ['A', 'B', 'X', 'Y', 'Z', 'C', 'D']
- 删除项
var arr = ['A', 'B', 'C', 'D', 'E', 'F', 'G'];
// 从下标为 2 的项开始,连续替换 4 项(替换为空,即:删除)。
arr.splice(2, 4);
console.log(arr);
// ['A', 'B', 'G']
splice() 方法会以数组形式返回被替换/删除的项。
var arr = ['A', 'B', 'C', 'D', 'E', 'F', 'G'];
// 从下标为 3 的项开始,连续替换 2 项。
var item = arr.splice(3, 2, 'X', 'Y', 'Z');
console.log(arr);
// ['A', 'B', 'C', 'X', 'Y', 'Z', 'F', 'G']
console.log(item);
// ['D', 'E']
var arr = ['A', 'B', 'C', 'D', 'E', 'F', 'G'];
// 从下标为 2 的项开始,连续替换 4 项(替换为空,即:删除)。
var item = arr.splice(2, 4);
console.log(arr);
// ['A', 'B', 'G']
console.log(item);
// ['C', 'D', 'E', 'F']
slice() 方法
slice()
方法用于得到子数组,类似于字符串中的 slice() 方法。
slice(a, b)
截取的子数组从下标为 a 的项开始,到下标为 b(但不包括下标为 b 的项)结束。
slice(a, b)
方法不会更改原有的数组。
slice()
如果不提供第二个参数,则表示从指定项开始,提取后续所有项作为子数组。
slice()
方法的参数允许为负数,表示数组的倒数第几项(记住不包括最后一项)。
var arr = ['A', 'B', 'C', 'D', 'E', 'F'];
var childArr1 = arr.slice(2, 5);
var childArr2 = arr.slice(2);
var childArr3 = arr.slice(2, -1);
console.log(arr); // ['A', 'B', 'C', 'D', 'E', 'F']
console.log(childArr1); // ['C', 'D', 'E']
console.log(childArr2); // ['C', 'D', 'E', 'F']
console.log(childArr3); // ['C', 'D', 'E']
join() 和 split() 方法
数组的 join()
方法可以使 数组 转为 字符串。
字符串的 split()
方法可以使 字符串 转为 数组。
-
join()
的参数表示以什么字符作为连接符,如果留空则默认以逗号分隔,如同调用toString()
方法。 -
split()
的参数表示以什么字符拆分字符串,一般不能留空。
[22, 33, 44, 55].join(); // "22,33,44,55"
[22, 33, 44, 55].toString(); // "22,33,44,55"
[22, 33, 44, 55].join(','); // "22,33,44,55"
[22, 33, 44, 55].join('-'); // "22-33-44-55"
[22, 33, 44, 55].join('~'); // "22~33~44~55"
'abcdefg'.split(); // ["abcdefg"]
'abcdefg'.split(''); // ["a", "b", "c", "d", "e", "f", "g"]
'a-b-c-d-e-f-g'.split(''); // ["a", "-", "b", "-", "c", "-", "d", "-", "e", "-", "f", "-", "g"]
'a-b-c-d-e-f-g'.split('-'); // ["a", "b", "c", "d", "e", "f", "g"]
concat() 方法
concat()
方法可以合并连接多个数组(以返回值的形式)。
concat()
方法不会改变原数组。
var arr1 = [1, 2, 3, 4];
var arr2 = [5, 6, 7, 8];
var arr3 = [9, 10, 11];
var arr = arr1.concat(arr2, arr3);
console.log(arr);
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
reverse() 方法
reverse()
方法用来将一个数组中的全部项顺序置反。
var arr = ['A', 'B', 'C', 'D'];
arr.reverse();
console.log(arr); // ["D", "C", "B", "A"]
indexOf() 和 includes() 方法
indexOf()
方法的功能是搜索数组中的元素,并返回它所在的位置,如果元素不存在,则返回 -1。
includes()
方法的功能是判断一个数组是否包含一个指定的值,返回一个布尔值。
['A', 'B', 'C', 'D'].indexOf('C'); // 2
['A', 'B', 'C', 'D'].indexOf('D'); // 3
['A', 'B', 'C', 'D'].indexOf('X'); // -1
['A', 'B', 'B', 'B'].indexOf('B'); // 1
['A', 'B', 'C', 'D'].includes('D'); // true
['A', 'B', 'C', 'D'].includes('X'); // false
[11, 22, 33].includes('22'); // false
[11, 22, 33].indexOf('22'); // -1
sort() 方法
sort()
方法用原地算法(直接改变原数组)对数组的元素进行排序,并返回数组。
默认排序顺序是将元素转换为字符串,然后根据字典序进行排序(数字 ——> 大写字母 ——> 小写字母,字符串内逐个字符进行比较,相同时比较下一位)
var arr = [3, 18, 10, 24];
console.log(arr.sort()); // [ 10, 18, 24, 3 ]
var arr = ['A', 'a', 'c', 'D', 1];
console.log(arr.sort()); // [ 1, 'A', 'D', 'a', 'c' ]
var arr = ['aa', 'a0', 'aA', 'A1', 'Aa', 'AA'];
console.log(arr.sort()); // [ 'A1', 'AA', 'Aa', 'a0', 'aA', 'aa' ]
var arr = [3, 18, 10, 24];
arr.sort(function(a, b) {
if (a > b) {
return -1;
} else if (a < b) {
return 1;
} else {
return 0;
}
});
console.log(arr); // [ 24, 18, 10, 3 ]