JS高级第二天

JavaScript高级第2天

一、构造函数和原型

1、对象的三种创建方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 使用构造函数创建对象
function People(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
this.say = function() {
console.log('hello');
}
}
var a = new People('小明', 13, '男');
console.log(a);
// 字面量
var obj = {
name: '小敏',
age: 12
};
// new Object()
var obj2 = new Object();
obj2.name = '小红';
obj2['age'] = 12;
console.log(obj2);

2、静态成员和实例成员

  • 静态成员:给构造函数添加的属性或方法, 只能通过构造函数本身才能访问的属性或方法
  • 实例成员:只能通过实例对象才能访问的属性或方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function People(uname, age, sex) {
this.uname = uname;
this.age = age;
this.sex = sex;
this.say = function() {
console.log('hello');
}
}
var a = new People('小明', 13, '男');
// 1. 实例成员: 只能通过实例对象才能访问的属性或方法
console.log(a.uname);
console.log(People.uname); //undefined ,原因是uname不在People内
// 2. 静态成员:给构造函数添加的属性或方法, 只能通过构造函数本身才能访问的属性或方法
People.height = 'height';
console.log(a.height); //undefined ,原因是height不在a内
console.log(People.height);

3、构造函数模型

  • 构造函数中直接添加方法的缺点

    每次创建对象,要重复开辟内存空间,浪费资源

  • 构造函数的原型 prototype

    每个函数默认都有一个 propotype 属性, 它的值默认是一个对象

    在 prototype 对象上的方法和属性, 会被 new 构造函数() 创建出来的实例对象所继承

  • 注意:

    ​ (1) 只要是函数就默认有 prototype 属性, 但非函数的对象是不具有的

    ​ (2) 定义构造函数时, 公共的方法定义在原型对象上, 这样可以被所有创建出来的实例直接继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function Student(uname, age) {
this.uname = name;
this.age = age;
}
// 每个函数默认都有一个 propotype 属性, 它的值默认是一个对象
Student.prototype.sing = function() {
console.log('哈哈哈哈');
}
Student.prototype.study = '学习JS';
var xm = new Student('小明', 15);
// 在prototype上添加的方法或属性会被所有的实例对象继承
console.log(xm.study);
xm.sing();
var obj = {
a: 1
};
// console.log(obj.prototype); //非函数没有此属性

4、对象模型

1
2
3
4
5
6
7
8
9
对象的原型
每个对象都默认有一个 __proto__的属性, 它的值是一个对象, 默认指向创建这个对象的构造函数的原型

对象原型的访问特点
每个对象访问__proto__下的所有属性和方法,可以省略__proto__

对象上读取属性的顺序
先从自身的属性上进行查找,如果没有再去__proto__属性指向的对象上去查找

img4

5、constructor构造函数

  • 对象原型( __proto__)和构造函数(prototype)原型对象里面都有一个属性 constructor 属性 ,constructor 我们称为构造函数,因为它指回构造函数本身。
  • constructor 主要用于记录该对象引用于哪个构造函数,它可以让原型对象重新指向原来的构造函数。
  • 当我们重新建立一个对象去覆盖原先的prototype时,constuctor属性需要我们手动赋值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 function Star(uname, age) {
this.uname = uname;
this.age = age;
}
// 很多情况下,我们需要手动的利用constructor 这个属性指回 原来的构造函数
Star.prototype = {
// 如果我们修改了原来的原型对象,给原型对象赋值的是一个对象,则必须手动的利用constructor指回原来的构造函数
constructor: Star, // 手动设置指回原来的构造函数
sing: function() {
console.log('我会唱歌');
},
movie: function() {
console.log('我会演电影');
}
}
var zxy = new Star('张学友', 19);
console.log(zxy)

6、原型链

每一个实例对象又有一个__proto__属性,指向的构造函数的原型对象,构造函数的原型对象也是一个对象,也有__proto__属性,这样一层一层往上找就形成了原型链。

img5

原型链理解(面试常问)

每个实例对象( object )都有一个私有属性(称之为 proto )指向创建它的构造函数的原型对象(prototype )。该原型对象也有一个自己的原型对象( proto ) ,层层向上直到一个对象的原型对象为 null。

7、原型链和成员的查找机制

对象访问属性时遵从原型链的顺序

​ (1) 当访问一个对象的属性时,首先从这个对象自身进行查找

​ (2) 如果没有找到就从它的原型对象(__proto__)中查找

​ (3) 如果还没有找到,继续沿着对象的原型链层层向上查找,直到最末尾的 null

8、原型对象中this指向

构造函数中的this和原型对象的this,都指向我们new出来的实例对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 非函数中this指向window
// 函数中this指向函数调用者
var that;
function Student(uname, age) {
// 函数中的 this 默认指向函数的调用者
this.uname = name;
this.age = age;
}
// 每个函数默认都有一个 propotype 属性, 它的值默认是一个对象
Student.prototype.sing = function() {
that = this;
console.log('哈哈哈哈');
}
Student.prototype.sleep = function() {
console.log(this === Student.prototype);
console.log('去睡觉');
}
var xm = new Student('小明', 15);
xm.sing();
console.log(that === xm);

// sing 中this指向调用者Student.prototype
Student.prototype.sleep();

9、原型链的应用:拓展内置对象上自定义方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var arr = [1, 2, 3]
console.log(arr.__proto__ === Array.prototype);
arr.push(4);

// 1. 需求:修改数组 Array 上的原型对象,添加自定义求和的方法
Array.prototype.getSum = function() {
console.log(this); //指向arr=[1,2,3,4]
var sum = 0;
for (var i = 0; i < this.length; i++) {
sum += this[i];
}
return sum;
}
console.log(arr.getSum());

拓展原型对象上的方法时,不能直接覆盖原来的原型对象,只能添加或和修改方法 注意:内置对象(如:Object, Array, Function) 上的原型对象不允许进行覆盖,只允许修改

二、继承

1、call()

  • 使用:fn.call([thisArg, arg1, arg2…])
  • 作用:调用一个函数,并指定调用时 this 的值
  • 参数: thisArg 函数中 this 的指定值; arg1, arg2…可选的参数列表
  • 返回值:函数调用的结果
  • 注意:第一个参数 thisArg 如果不传或是 null、undefined,默认函数内 this 指向 window

2、子构造函数继承父构造函数中的属性

  1. 先定义一个父构造函数
  2. 再定义一个子构造函数
  3. 子构造函数继承父构造函数的属性(使用call方法)
1
2
3
4
5
6
7
8
9
10
11
12
13
// 子类继承父类属性的核心: 使用 call() 把父类的实例对象 this 手动修改为子类的实例对象 this
function People(name, age) {
console.log(this); //this指向父类创建的实例对象
this.name = name;
this.age = age;
}

function Student(name, age) {
// this指向子类创建的实例对象
People.call(this, name, age);
}
var xm = new Student('小明', 15);
console.log(xm);

3、借用原型对象继承方法

  1. 先定义一个父构造函数
  2. 再定义一个子构造函数
  3. 子构造函数继承父构造函数的属性(使用call方法)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
//  子类继承父类上方法的核心: 新建一个对象作为子类的原型对象, 并把这个对象的原型指向父类的 prototype
function People(name, age) {
console.log(this); //this指向父类创建的实例对象
this.name = name;
this.age = age;
};
People.prototype.say = function() {
console.log('hello');
};

function Student(name, age, subject) {
// this指向子类创建的实例对象
People.call(this, name, age);
this.subject = subject;
};
// 三种方法可以调用父类方法:
// (1)直接把父构造函数的原型拿过来用,但是会导致子类添加的方法,父类也会有
Student.prototype = People.prototype;

// 2) 不能把两个原型放在一个空间, 需要创建一个新的内存空间
var obj = {
constructor: Student, //把constructor指向子构造函数
__proto__: People.prototype, //通过原型链,把父构造函数的原型直接拿过来;
}
Student.prototype = obj;

// (3)实例化父级的构造函数
Student.prototype = new People();
Student.prototype.constructor = Student;

// 子类构造函数原型上添加新的方法
Student.prototype.sleep = function() {
console.log('去睡觉啊');
}
var xm = new Student('小明', 15, '语文');
xm.say(); //xm直接访问父构造函数原型上的方法;
console.log(xm);
console.log(People.prototype); //检测父构造函数上的原型是否被子构造函数修改
console.log(xm.constructor); //检测xm是谁创建出来的

4、类的本质

  1. 构造函数默认有一个 prototype 属性, 它的值是一个对象
  2. 构造函数 prototype 属性下有 constructor 和 proto 两个默认的属性
  3. 构造函数可以通过在 prototype 上添加方法, 去实现所有实例继承
  4. 构造函数创建的实例上 __proto__指向创建它构造函数的 prototype
  5. class 类的本质其实就是一个改造版的构造函数

三、ES5新增数组方法

1、数组方法forEach遍历数组

  • 参数:callback(元素, 索引, 数组本身)
  • 返回值:无
  • 例子: 使用 forEach 对数组求和
1
2
3
4
 var arr = [10, 12, 55, 13]
arr.forEach(function(item, index, arr) {
console.log('索引号:' + index + '===>' + '数组元素:' + item);
})

2、filter() 方法从数组筛选出符合条件的所有元素

  • 参数:callback(元素, 索引, 数组本身)
  • 返回值:新数组
  • 例子: 使用 filter 筛选数组中所有偶数
1
2
3
4
5
6
 var arr2 = [1, 2, 3, 4, 5, 6];
var newArr = arr2.filter(function(item,index) {
// 如果return后面的结果为真,就返回到新数组中
return item % 2 == 0;
})
console.log(newArr);

3、some() 方法用于查找数组中是否有符合条件的某一个元素

  • 参数:callback(元素, 索引, 数组本身)
  • 返回值:true(找到) || false(找不到)
1
2
3
4
5
var arr = ['hello', '12', ' ', 'true', '']
var a = arr.some(function(item, index){
return item == '';
})
console.log(a);

4、 every() 方法用于查找数组中是否所有的元素都符合条件

  • 参数:callback(元素, 索引, 数组本身)
  • 返回值:true || false (只要有一个不满足就返回 false)
1
2
3
4
5
6
7
var arr3 = [30, 22, 45, 16, 20];
var b = arr3.every(function(item, index) {
return item > 20;
})
console.log(b);
// 这些方法本质上都是存在原型上 Array.prototype;
console.log(arr3.__proto__ === Array.prototype); //true

5、trim方法去除字符串两端的空格

1
2
3
4
var str = '   hello   '
console.log(str.trim()) //hello 去除两端空格
var str1 = ' he l l o '
console.log(str.trim()) //he l l o 去除两端空格

6、获取对象的属性名

Object.keys(对象) 获取到当前对象中的属性名

  • 返回值是所有属性名组成的数组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var obj = {
a: 1,
b: 2,
c: 3
};
for (var key in obj) {
console.log(obj[key]);
}
// 1. 使用 Object.keys() 方法获取对象上所有的属性名
// 返回值:所有属性名组成的数组
var res = Object.keys(obj);
console.log(res);

// 2. 新遍历对象的方法
res.forEach(function(item) {
console.log(item); //获取的是属性名
console.log(obj[item]); //获取属姓名对应的值,注意都是变量,要用[]
})

7、Object.defineProperty() 方法

  • 作用:在对象上定义一个新属性,或者修改原属性,并返回对象

  • 使用方法:

    Object.defineProperty(obj, prop, descriptor)

    ​ (1) obj: 要定义属性的对象

    ​ (2) prop: 要定义或修改的属性的名称, 字符串 ‘name’

    ​ (3) descriptor: 要定义或修改的属性描述符, 数据格式是对象 {}

  • 属性的描述符:

    (1) value: 属性值

    (2) writable: 是否可以修改

    (3) enumerable: 是否可以枚举(遍历到)

    (4) configurable: 是否可以删除或再次修改特性

    ​ 三个属性描述符默认都是 false

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var obj = {
name: '张三',
age: '12',
gender: 'man'
}
Object.defineProperty(obj, 'height', {
value: '170',
writable: true, //属性可以修改
enumerable: true, //可以遍历到
configurable: true //可以再次修改
})
obj.height = '175';
console.log(Object.keys(obj));
// 删除属性 delete;
delete obj.height;
console.log(obj.height);

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!