全栈记

08 js03 对象、包装类

2018-10-12  本文已影响5人  官清岁月

1、对象:【对象的属性和方法,方法存在的形式:属性为方法名/引用;属性值为函数体,对象的属性值可以是任何类型,原型值/引用值都OK,后续使用引用值情况很多】

对象的创建方法:

(1).var obj = {} // plainObject   对象字面量/对象直接量;[所谓字面量就是从表面去判断其是什么:例如var str ="abc"; 字面量就是字符串;数组、正则表达式也会有对象字面量这样的称呼]

(2).构造函数:1.系统自带的构造函数: new Object(); new Array();  new Function(); new Number(); new String(); new Boolean();2.自定义构造函数(有参数/无参数);

[1].构造函数创建对象:构造函数类似于一个模具厂,其创建的对象外表可以一模一样(无参数),但每个对象都是相互独立存在的,往往我们把对象不同的属性值作为参数进行区分不同的对象【java c++ 这类面向对象语言产生的对象和js产生的对象的差别:Java、C++产生对象时根据模版也就是类来创建对象,产生的对象属性是死的,不能动态的改变,但js产生的对象可以动态的进行更改(增加、删除、修改、查看属性)】

[2].自定义构造函数:函数名采用大驼峰命名规则【构造函数的函数名采用小驼峰规则也是OK的,但为了增加区分度,还是使用大驼峰规则最佳】构造函数也是函数;

[3].var person = new Person(); // new+函数执行;--->>> new Person(); 若是没有new操作符,其就是普通的函数执行而已(普通的函数执行,那自然是可以传入参数的,var person = new Person("xiaowang",18);慢慢熟悉),使用了new操作符就可以创建对象,底层原理:构造函数的隐式三段论;

(3).Object.create(原型):

(4).ES6新语法class:

补充:【构造函数也是函数,功能聚焦点为创建对象】

构造函数创建对象底层原理:为什么普通的函数执行+new操作符便可以创建对象以及为何使用this?【没有new操作符,普通函数执行;若有了new操作符,其在构造函数内部会执行三段论】

隐式三段论:1.在函数体最前面隐式的添加this{} 空对象;2.执行里面的this.xxx = xxx; 3.隐式的返回this对象 return this;[this的作用表示的this{};若是没有this,则不清楚里面的属性是谁的]

当然也可以不使用变量接收,直接使用,console.log(new Person("xiaobei".name);

测试:显示加上return {};

//当然也可以模仿隐式三段论来创建对象,不过三段论底层还有更深入的机制,this{}对象里是有属性的,例如后面学到的原型链属性;this的使用场景很广泛,后续使用this会非常便捷,解决很多问题;

2、包装类:new Number(); new String(); new Boolean(); [js中只有对象才有属性和方法,原始值没有属性和方法,但其可调用包装类成为对象,之后便可操作自己的属性和方法] [原始值里的undefined/null是没有包装类的也就永远都是原始值,不能成为对象即没有属性和方法]

(1).使用方法:var num = 123//数字123; var num = new Number(123);//对象123;同理new String("skrskr");  var myBoolean=new Boolean(null);//创建初始化为false的Boolean对象【可创建初始化为true/false的Boolean对象,有6个值是false,其余都为true,改变参数即可】

【特殊现象:var num = new Number(123); 此时num是对象,但当它进行*2操作时并没有报错,其可以回归为原始值,但它是对象,也有属性和方法,  推测应该内部发生隐式类型转换,不用深究】

(2). 可以解决一类现象:string.length;//不报错,而且会返回其的长度;原始值是没有属性和方法的,底层原理:隐式调用了自己的包装类;new String(string).length; [平时谈到的字符串是指原始值字符串,包装类:对象字符串]

补充:

3、原型、原型链:

【使用原型/原型链的原因:1.构造函数创建对象相比于字面量创建对象更加便捷,节省内存,其可批量创建对象,若使用字面量,创建的每个对象都要重新写一遍;2.构造函数中的属性和方法在实例化的过程中会占用大量内存空间,而且构造函数中也不可能填充太多的属性和方法,所以建议把共享的属性和方法放到对象的原型上,这样实例化对象,只会占用一份内存空间】

(1).原型:prototype,其是function对象的属性(函数创建时刻便生成),聚焦点为构造函数,它定义了构造函数制造出的对象的公共祖先,通过该构造函数产生的对象,可继承该原型的属性和方法,它也是对象;【子孙对祖父属性的操作是无效,它只能操作自己的属性/属性值】】

【原型上的属性和方法在控制台上不是直接显示的,是通过原型链折叠起来的,构造函数内的属性/属性值是直接显示出来的】

(2).原型链:对象都有该属性__proto__; 其指向原型,默认指向构造函数的原型;【new+函数执行创建对象,该对象查找属性,若是自身构造函数中没有便沿着原型链查找,其是如何和其他原型产生联系的?通过构造函数隐式三段论中的this{};对象中的原型链属性】【__proto__其指向的是对象,从而可实现继承其他对象的属性/属性值】

【之前创建对象中谈到this{}对象是空的,其内部实际有个隐式的属性__proto__:指向原型】

原型链的增、删、改、查:同原型情况相同,对象只能修改自身的属性/属性值,无法修改父辈、祖父辈[对象查找一个属性,若是没有,向上查找父辈,找到了修改的时候,并不能修改人家原有的属性]

延生问题:

补充:

[1].对象都有__proto__属性(系统定义);原型都有两个属性:constructor、_proto__;(系统定义);

浅粉色代表系统隐式定义

对象.constructor;//返回对象的构造函数(对象可查看自己的构造器,清楚自己是怎么来的)

[2].Object.create(原型); [第三种创建对象的方法] [ 原型就是对象,参数填入对象即可]

【Object.create(prototype,definedProperty);第一个参数是原型,第二个参数是特性; 暂时掌握原型就OK】

原型只能是对象或null值,不能是原始值,为null时创建的对象没有原型;【没有原型的时候可手动添加person.__proto__:{name:"xiao"};但生成的对象时不能继承该属性的,就相当于给自己加属性而已】

无原型的时候不能手动创建原型,有原型的时候可以操作更改原型内容;

---->>>Object.create(null);//可创建新对象,但创建的对象没有原型(原型链),

[3].绝大多数对象最终都会继承自Object.prototype;(祖先原型)(对) 上述所说的Object.create(null)创建的对象没有原型; [var obj = Object.create(123);//报错,原始值必然不行]

[4].关于var obj = {}; var obj1 = new Object(); 两种创建对象的方式不同,var obj = {};感觉其是没有原型的,因为其不是构造函数构造出来的,但测试发现两者原型链指向都是Object;

var obj = {};// 其是有原型的,等同于隐式new Object(); [日常开发中两种方式中要求使用字面量的方式,其更加便捷,若是使用new Object(); 增加属性需要在Object.prototype上书写,而且其是所有对象的终端,不能随便增添属性]

[构造函数的原型默认是空对象,Person.prototype = {} 这就是字面量空对象,其的原型链指向自然是Object.prototype终端]

补充:var obj1 = new Object();  obj1.__proto__:Object.prototype; 但控制台显示的是Object,两者是相同的,只是表现形式不同而已;【后续经常会在原型链上进行编程操作,Array.prototype】

[5]toString();方法,其是Object对象的一个方法,所以绝大多数对象都会有此方法,除了Object.create(null),以及原始值undefined/null,[number/string/boolean等可隐式调用包装类]

特殊现象1:日常使用toString()方法,其可以转换为字符串,但直接使用Object.toString();方法返回的值却是如下的方式,并不是日常使用后的返回值,这种现象是因为发生了方法的重写;重写是指写一个名字相同但是功能不同的函数;Object原型链的终端虽然有toString();方法,但是它们进行了重写:Number.prototype.toString; Array.prototype.toString; Boolean.prototype.toString; String.prototype.toString;

特殊现象2:document.write();  //打印的底层原理,调用的是toString()方法;

var obj = {}; document.write(obj);//[object object];  document.write(123);//打印出的是字符串123;

特殊现象3:123.toString(); 会报错,使用变量接收不报错;原因系统会识别为数字的浮点运算,后面写字母肯定不行,其不能优先识别为是方法调用;【true.toString(); 可以】

4、call();/apply();  【Object.prototype.toString.call();//原型上的toString();调用call(); 必须使用方法名调用;不能是Object.prototype.toString().call();//报错;[call();方法可被任何函数调用改变this指向]】

[其是Function.prototype.call();原型链上的方法,所有方法都可以调用,后续还会学到bind();方法,bind();改变this指向不会执行,call();/apply();改变this指向并且会立刻执行,后续剖析源码]

(1).作用:改变this指向,区别就是传参形式不同;[call 需要把实参按照形参个数传进去,apply 需要传一个数组,arguments;]

(2).函数都有此方法,应用场景更多是函数调用;[借用别人的函数实现自己的功能(我需要设计的函数覆盖了别人设计的函数,常用于组合开发)]

Person.call(this,name,age,sex);执行过程会把Person函数的语句全部执行一次,传入参数个数要相同,即使有的属性不想使用,传入undefined即可,不过使用的场景就是覆盖了某函数的属性才调用

模块组合开发,实际开发要更加复杂(this.xxx = xxx;这里的xxx可能是一个很复杂的函数,立即执行函数等等)

补充:test(); --->>> 内部隐式是test.call();  this指向问题很重要; 函数执行时this默认指向是window;new test(); this指向为this{}; 当test.call(内容);当有内容的时候,其就可以改变this指向;没内容就等同于直接函数执行test();

上一篇下一篇

猜你喜欢

热点阅读