一、深入原型
- 普通函数默认返回的是undefined; return undefined
- 构造函数在实例化对象的时候,系统默认返回的是this; return this
- 原型的原型一定是由Object这个基类所构造出来的
- 不是所有的对象都继承与Object.prototype,如果是基于Object.create(null)创建出来的对象,它是没有原型的;是一个纯粹的空对象
- null和undefined都是不能调用toString方法的,因为两个都是属于原始值,原始值是没有属性或者是方法的,且无法进行包装类,有没有原型;所以说他们不能继承Object.prototype上的toString方法
- 系统内置的包装类上都有toString方法,作用是将其装换为字符串,包装类是将这个方法重写了的;Object.prototype上也有toString方法,不过这个是用于数据类型检测的
- proto:适用于保存原型的容器
- 原型的最顶端是Object.prototype
- 原型链:沿着prototype这条线去寻找原型上的属性或者方法,一层一层的去继承;这个链条就叫做原型链
- 原型链上的属性或者是方法,想要增删改,只能是它自己本身;后代是无法增删改其原型链上的属性或者是方法的
document.write打印出来的是是字符串的格式,alert输出的也是字符串格式
// 深入原型和原型链// 普通函数默认返回的是undefined; return undefined// 构造函数在实例化对象的时候,系统默认返回的是this; return this// 原型的原型一定是由Object这个基类所构造出来的// 不是所有的对象都继承与Object.prototype,如果是基于Object.create(null)创建出来的对象,它是没有原型的;是一个纯粹的空对象// null和undefined都是不能调用toString方法的,因为两个都是属于原始值,原始值是没有属性或者是方法的,且无法进行包装类,有没有原型;所以说他们不能继承Object.prototype上的toString方法// 系统内置的包装类上都有toString方法,作用是将其装换为字符串,包装类是将这个方法重写了的;Object.prototype上也有toString方法,不过这个是用于数据类型检测的// __proto__:适用于保存原型的容器// 原型的最顶端是Object.prototype// 原型链:沿着__prototype__这条线去寻找原型上的属性或者方法,一层一层的去继承;这个链条就叫做原型链// 原型链上的属性或者是方法,想要增删改,只能是它自己本身;后代是无法增删改其原型链上的属性或者是方法的// document.write打印出来的是是字符串的格式,alert输出的也是字符串格式function Car(){}var car = new Car();console.log(car);console.log(Car.prototype);
二、原型链和原型继承
2.1、JS中的原型继承和原型链
原型本身也有原型
- proto是用来保存原型的一个容器
- 原型链:沿着proto去寻找原型上的属性或者是方法,一层一层的去继承,这个链条就叫做原型链
原型链的最顶端是Object.prototype
// 原型链:沿着__proto__去寻找原型上的属性或者是方法,一层一层的去继承,这个链条就叫做原型链// 原型继承Professor.prototype.tSkill = 'JAVA';function Professor(){}var professor = new Professor;Teacher.prototype = professor;function Teacher(){this.mSkill = 'JS/JQ';}var teacher = new Teacher();Student.prototype = teacher;function Student(){this.pSkill = 'HTML/CSS';}var student = new Student();console.log(student);console.log(student.mSkill);console.log(student.tSkill);
2.2、原型链上的属性或方法是否能够修改
Object.prototype上有一个toString方法,改变其this指向,可以用来检测数据类型
- 原型链上的属性或者是方法想要实现增删改,原则上只能是自己去做,后代原则上是无法修改原型链上的属性和方法的
- 原型链上的属性如果是原始值,则后代无法去修改;但是要是该属性为引用类型,则可以修改
如果原型链上的属性是引用类型的,那么后代是可以增删改原型链上的属性或者是方法的,但是我们并不建议这么去做
var obj = {age: 18}obj.name = 'kola';console.log(obj);
原型链上的属性如果是原始值,则后代无法去修改;但是要是该属性为引用类型,则可以修改
// 型链上的属性如果是原始值,则后代无法去修改;但是要是该属性为引用类型,则可以修改// 如果原型链上的属性是引用类型的,那么后代是可以增删改原型链上的属性或者是方法的,但是我们并不建议这么去做Professor.prototype.mSkill = 'JAVA';function Professor(){}var professor = new Professor();Teacher.prototype = professor;function Teacher(){this.mSkill = 'JS/JQ';this.scuccess = {alibaba: 28,tencent: 10}}var teacher = new Teacher();Student.prototype = teacher;function Student(){this.pSkill = 'HTML/CSS';}var student = new Student();student.scuccess.baidu = 30;student.scuccess.alibaba = 90;console.log(teacher);console.log(student);

// 如果原型上的属性是原始值,那么后代是不可以修改原型上的属性的Professor.prototype.mSkill = 'JAVA';function Professor(){}var professor = new Professor();Teacher.prototype = professor;function Teacher(){this.mSkill = 'JS/JQ';this.students= 500;}var teacher = new Teacher();Student.prototype = teacher;function Student(){this.pSkill = 'HTML/CSS';}var student = new Student();student.students++;// student.students++ => student.students = student.students+1// student = {// students:// }console.log(teacher);console.log(student);
三、this指向问题
this指向:普通函数执行,看前面有没有点,点前面是谁this就指向谁
谁用this就指向谁
// this指向问题:普通函数执行,看前面有没有点,有点,点前面是谁this就指向谁function Car(){this.brand = 'Benz'}Car.prototype = {brand: 'Mazda',instro: function(){console.log('我是' + this.brand + '车');}}var car = new Car();car.instro();// 谁用this就指向谁Car.prototype.instro();console.log(Car.prototype.brand);
四、构造函数实例化后默认返回this
构造函数在实例化对象后默认返回的是this「系统默认返回的」;return this
普通函数默认返回的是undefined; return undefined
// 构造函数在实例化对象后默认返回的是this「系统默认返回的」;return this// 普通函数默认返回的是undefined; return undefinedfunction Person(){this.somke = function(){this.weight--;}}Person.prototype = {weight: 130}var person = new Person();person.somke();console.log(Person.prototype);console.log(Person);
五、声明对象的两种方式
字面量
系统自带的构造函数方式
// 声明对象的两种方式// 字面量方式// 系统自带的构造函数方式// 两者声明出的对象没有什么区别var obj1 = {};var obj2 = new Object(); // 公司不用这种console.log(obj1, obj2);// 通过构造函数实例化对象function Obj(){}var obj3 = new Obj();console.log(obj3);
六、原型的原型一定指向系统构造函数
// 原型的原型一定指向系统自带的构造函数function Obj(){}var obj = new Obj();console.log(Obj.__proto__); // ƒ () { [native code] }
七、Object.create(原型对象,null)
Object.create(原型对象);创建一个空对象,可以自己定义原型,不用使用系统自带的原型
Object.create(null);可以创建一个纯粹的空对象,空对象里面什么属性也没有
// Object.create(原型对象); 创建一个空对象,可以自己定义原型,不用使用系统自带的原型// Object.create(null); 创建一个纯粹的空对象,对象里什么属性都没有var test = {num: 2,name: 'kola'}function Obj(){}Obj.prototype.num = 1;var obj1 = Object.create(test);var obj2 = new Obj();console.log(obj1);console.log(obj2);

Object.create(null);可以创建一个纯粹的空对象,空对象里面什么属性也没有
var obj1 = Object.create(null);console.log(obj1);

var obj1 = Object.create(null);console.log(obj1);obj1.num = 100;var obj2 = Object.create(obj1);console.log(obj2);console.log(obj2.num);
八、自己指定proto没有用,这个属性必须是系统的
自己执行proto是没有用的,proto属性必须是系统自带的
- proto必须是系统内置的,可以更改它,但是不可以自造
不是所有的对象都继承与Object.prototype,比如说基于Object.create(null)所创建出来的对象
// 自己指定__proto__是没有用的,proto属性必须是系统自带的var obj = Object.create(null);obj.num = 100;console.log(obj); // 这是一个没有原型的对象var obj1 = {count: 2}obj.__proto__ = obj1; // proto必须是系统内置的,可以更改它,但是不可以自造console.log(obj);
九、null和undefined无法使用toString方法
null和undefined都属于原始值类型,原始值类型是没有属性和方法的
- null和undefined又无法通过系统的包装类将其包装为对象,因为没有变量去保存
unll的undefined没有原型,所以无法继承原型上的toString方法
// null和undefined无法调用toString方法// 因为null和undefined都是属于原始值,原始值是没有属性和方法的// null和undefined无法通过系统的包装类将其包装为象,因为没有变量去保存// unll和undefined都没有原型,所以无法继承原型上的toString方法console.log(null.toString()); // 报错console.log(undefined.toString()); // 报错
十、系统包装类上的toString方法和Object.prototype上的toString方法的区别
系统包装类原型上的toString方法的作用是将值转换为字符串
Object.prototype上的toString方法,通过改变其this指向,可以实现数据类型检测「万能的数据类型检测」
// 系统包装类原型上的toString方法和Object.prototype上的toString方法作用是不一样的var num = 100;console.log(num.toString()); // 原始值没有属性和方法,这里是经过了系统的包装类,并且有变量保存
原始值没有属性和方法,系统内置的包装类的原型上都有toString方法,作用是将值转换为字符串
// 原始值没有属性和方法,系统内置的包装类的原型上都有toString方法,作用是将值转换为字符串var num1 = 110;var num2 = new Number(num1);console.log(num2.toString());console.log(num2);
document.write输出的都是字符串格式,在打印的过程中存在隐式转换,会将值转换为字符串再打印出
Object.create(null)创建的是一个纯粹的空对象,没有原型
// document.write打印的过程中存在着隐式类型转换,会将值转换为字符串格式var num = 100;var obj = {}var obj2 = Object.create(null);document.write(num); // '100'document.write(obj); // [object Object]// 首先基于Object.prototype创建出来的对象是没有原型的,它是一个纯粹的空对象// 既然没有原型,那它就不能继承原型上的toString方法,就不能将obj2转换为字符串// 然而document.write输出的结果又必须是字符串,所以就只能报错了document.write(obj2); // 报错 Cannot convert object to primitive value
系统包装类的原型上都有toString方法,作用是将值转换为字符串
Object.prototype上也有toString方法,通过改变其this指向,可以实现数据类型检测「万能的」
// 系统包装类上都有toSting方法,作用是将值转换为字符串// Object.prototype上也有toString方法,通过改变其this指向可以实现万能数据类型检测的功能console.log(Object.prototype.toString.call(1)); // [object Number]console.log(Object.prototype.toString.call(true)); // [object Boolean]console.log(Object.prototype.toString.call([])); // [object Array]console.log(Object.prototype.toString.call({})); // [object String]console.log(Number.prototype.toString.call(123)); // 124console.log(Boolean.prototype.toString.call(true)) // 'true'
十一、call和apply的作用
相同点:都是为了改变其this的指向
- 不同点:call传递参数时是一项一项去传递的;而apply传递参数时是以数组的形式传递
在使用小括号执行符号时,系统会隐式加上call
// call和apply的作用// 两者都是为了改变其this的指向// call传递参数时是一项一项的去传递的// apply传递参数是以数组的形式传递的function test(){console.log('a');}test(); // 'a'// 在使用执行符号时,系统会隐式加上calltest.call(); // 'a'
```javascript
// call改变其this指向: 在传递参数时是一项一项去传递的function Car(brand, color){this.brand = brand;this.color = color;}var newCar = {}Car.call(newCar, 'Benz', '黑色');console.log(newCar); // {brand: 'Benz', color: '黑色'}
// apply改变其this指向: 在传递参数时是以数组形式传递的function Car(brand, color){this.brand = brand;this.color = color;}var newCar = {displacement: '3.0'}Car.apply(newCar, ['Mazda', '红色'])console.log(newCar); // {displacement: '3.0', brand: 'Mazda', color: '红色'}var car = new Car('比亚迪', '白色');console.log(car); // Car {brand: '比亚迪', color: '白色'}
实例```javascriptfunction Compute(){this.add = function(a, b){console.log(a + b);},this.minus = function(a, b){console.log(a - b);}}function FullCompute(){// console.log(this);Compute.call(this);this.mul = function(a, b){console.log(a * b);},this.div = function(a, b){console.log(a / b);}}var compute = new FullCompute();compute.add(2, 4);compute.minus(2, 4);compute.mul(2, 4);compute.div(2, 4);
十二、作业
年龄为多少岁,姓名为xxx买了一辆排量为多少的什么颜色什么牌子的车「利用call或者apply改变this指向」
// 年龄为多少岁,姓名为xxx买了一辆排量为多少的什么颜色什么牌子的车「利用call或者apply改变this指向」function Car(opt){this.brand = opt.brand;this.color = opt.color;this.displacement = opt.displacement;}function Person(opt){this.name = opt.name;this.age = opt.age;this.selectCar = function(){Car.apply(this, [{brand: 'Benz', color: '黑色', displacement:'2.0'}]);console.log('年龄为:' + this.age + '姓名为:' + this.name + '买了一辆排量为:' + this.displacement + '的' + this.color + this.brand);}}var person = new Person({name: '小明',age: 18});person.selectCar();
// 11课时作业function Car(brand, color, displacement){this.brand = brand;this.color = color;this.displacement = displacement;this.info = function(){return '排量为' + this.displacement + '的' + this.color + this.brand;}}function Person(opt){Car.apply(this, [opt.brand, opt.color, opt.displacement]);this.name = opt.name;this.age = opt.age;this.say = function(){console.log('年龄为' + this.age + '岁姓名为' + this.name + '买了一辆' + this.info());}}var person = new Person({brand: '奔驰',color: '红色',displacement: '3.0',name: '约翰',age: 28});person.say();
