web前端开发笔记整理(四)

Orenda ·
更新时间:2024-11-10
· 513 次阅读

文章目录对象構造函數包装类原型原型链toString()方法call/apply 对象

JavaScript不区分类和实例的概念
创建一个name对象

var name=“小明”;

创建一个函数的时候,函数也是对象

function foo() { return 0; }

创建对象的两种方式

var obj = new object(); var obj = {};

写个小demo感受一下:

var test={ name:"xiaohua", age:"18", sex:"male", health:100, smoke:function(){ console.log("i am smoking") this.health--; console.log(this.health) }, drink: function(){ console.log("i am drining") this.health++; } }

这里创建了一个叫test的对象,里面装了叫小花的人名,性别,性别。
定义了两个函数体,分别用来控制小花的健康,抽烟呢健康就在100的基础上-1,喝酒呢就加一。

我们在控制台查看
在这里插入图片描述

对象的增删查改

在上面的代码上进行

”增加“
在这里插入图片描述
"删除"
一定要用delete

在这里插入图片描述

"查找"
在这里插入图片描述
”改”
也就是在原有基础上给变量赋值;
在这里插入图片描述

構造函數

构造函数 ,是一种特殊的方法。主要用来在创建对象时初始化对象。 即为对象变量赋初始值。每个构造函数的实例都将共享构造函数的初始值。 构造函数的出现是为了解决使用Object构造函数和字面量表示法不方便创建大量重复对象的问题。

构造函数的内部原理

在函数体前面隐式的加上this = {};

执行this.xxx = xxx ;

隐式返回this

构造函数的定义的格式:

修饰符 函数名(形式参数){ 函数体... } 构造函数 是没有返回值类型的。 构造函数的函数名必须要与类名一致 构造函数需要以一个大写字母开头,而非构造函数应该以一个小写字母开头,这个主要是为了区别构造函数和其它函数 构造函数其实本身也是函数,只是用来创建对象 调用构造函数千万不要忘记写new 如果一个类没有显式的写上一个构造方法时,那么java编译器会为该类添加一个无参的构造函数的。

任何函数,只要通过 new 操作符来调用,那它就可以作为构造函数 ;

function Student(name) { this.name = name; //只在被实例化后的实例中可调用 this.hello = function () { alert('Hello, ' + this.name + '!'); } } var xiaoming = new Student('小明');//this指向小明 xiaoming.name; // '小明' xiaoming.hello(); // Hello, 小明!

在这里插入图片描述

普通函数与构造函数的区别“

1. 返回值类型的区别:

1. 构造函数是没有返回值类型 的, 2. 普通函数是有返回值类型的,即使函数没有返回值,返回值类型也要写上void。

2. 函数名的区别:

1. 构造函数的函数名必须要与类名一致, 2. 普通函数的函数名只要符合标识符的命名规则即可。

3. 调用方式的区别:

1. 构造函数是 在创建对象的时候由jvm调用的。 2. 普通函数是由我们使用对象调用的,一个对象可以对象多次普通 的函数,

4. 作用上的区别:

1. 构造函数 的作用用于初始化一个对象。 2. 普通函数是用于描述一类事物的公共行为的。 包装类

原始值没有属性和方法,但是如果强行添加属性或者访问属性的话,系统就会新建一个包装类,然后在包装类上进行操作,操作完成后进行销毁

JS的数据类型:

**值类型(基本类型)**字符串(String)、数字(Number)、布尔(Boolean)、对空(Null)、未定义(Undefined)、Symbol。

引用数据类型对象(Object)、数组(Array)、函数(Function)。

JS中只有对象才有属性和方法,原始值没有属性和方法
给基本类型添加属性和方法:

//给字符串添加方法 要写到对应的包装对象的原型下才行 var str= "hello world"; //若我们想在原型上设置一个属性long 保存字符串的长度 //var str = new String("hello world");// 1.找到基本包装对象,创建一个和字符串值相同的对象, //String.prototype.long=str.length; // 2.通过这个对象找到了包装对象下的方法并调用 //str=null; // 3.这个刚创建的对象被销毁 String.prototype.long=str.length;// 执行到这一句,同样因为没有length属性 后台会偷偷的执行上述三步操作 console.log(str.long); //结果为:11 var str= "hello world"; var str2="我的长度也为11吗?"; String.prototype.long=str.length; console.log(str2.long); //结果为:11

这样因为你是给基本类型string的原型上添加的属性,所以任意一个字符串都可以访问到这个属性及此值。(所以不提倡这种做法)

原始值没自定义有属性和方法 不能给原始值赋值(赋值也不会报错)
js中提供了三种特殊的引用类型(String Number Boolean)每当我们给原始值赋属性值时 后台自动调用包装类转换
String 默认有length属性而且不可赋值

var str = 'abc'; str+=1; //str = 'abc1'; var test = typeof str;//test = 'string'; if(test.length == 6){ // var obj = new String(;'abcd1'); // obj.sing = true; // delete obj; test.sign= true; } // var obj = new String(;'abcd1'); // console.log(obj.sing); // delete obj; console.log(test.sign);// undefined

在这里插入图片描述

test是字符串string test.length == 6 就是对的, test.sign会把test调用,然后test.sign输出true???
但是原始值是不能操作属性的,原始值赋属性值要调用包装类

test是原始值,不会有length属性
代码执行在test.sign处会new string(test).sign=true
进行包装类之后就会删除销毁,所以到最后一句系统看test原始值没有sign属性,又会包装一遍new string(test).sign。

但这次操作跟上次操作不一样,系统执行语句进行包装类,执行完包装类之后就会销毁,在执行语句又会在创建包装类,那么你的这个新的包装类中并没有sign的属性。

因为进行了包装类已经将test变成一个构造对象了,打印对象中的属性,即使我并没有这个属性,系统一样不会报错,只会提示undefined。
所以输出undefied

var x =1,y=z=0; function add(n){ return n =n+1; } y=add(x); function add(n){ return n = n+3; } z=add(x);

xyz分别输出
输出1,4,4,
这里存在一个函数提升,第二个add(n)覆盖第一个add(n)

原型 原型是function对象的一个属性,它定义了构造函数制造出的对象的公共先祖。 通过该构造函数产生的对象,可以继承该原型的方法。 原型也是对象 原型的应用

提取共有属性,避免代码冗余
例如

function Car(color,owner){ this.color = color; this.owner = owner; this.carName = "bmw"; this.height = 1400; this.lang = 4900; } var car = new Car("red","bmw",1400,4900,"prof.ji"); var car1 = new Car("blue","bmw",1400,4900,"long");

在这段代码中car和car1和carName属性值都是相同的,我们可以把这些属性赋给函数原型

Person.prototype = { height:1200; lang:4900; carName : "bmw"; } function Car(color,owner){ this.owner = owner; this.color = color; } var car = new Car("red","prof.ji"); var car1 = new Car("blue","long");

对象修改原型的属性时只能通过调用原型的属性进行改变

Person.prototype.lastname = "deng"; function Person(name){ this.name = name; } var person = new Person("sumong"); Person.prototype.lastname = "liu"; //改变原型的属性 原型的几个属性:

prototype属性:我们只要创建了一个函数,就会根据一组特定的规则为这个函数创建一个prototype属性。这个属性指向函数的原型对象。

constructor属性:创建了一个自定义构造函数后,其原型对象默认只会取得constructor属性。指向了构造函数。

_proto_属性:(这是一种隐式命名规则,是系统命名的): 这个属性指向了构造方法的原型对象。

1.prototype Person.prototype.name="xiaohua"; function Person(){ } var person =new Person(); var person1=new Person();

在这里原型是Person.prototype
祖先是Person.prototype={ }
在这里插入图片描述

new Person()创建多个对象person、person1,则多个对象都会同时指向Person构造函数的原型对象。 prototype在构造函数出生的时候就已经被定义好了 如果我们访问person中的一个属性name,如果在person对象中找到,则直接返回。如果person对象中没有找到,则直接去person对象的__proto__属性指向的原型对象中查找 原型相当于函数产生对象的隐形父级,是可以访问的 2.constructor查看对象的构造函数

任何一个prototype对象都有一个constructor属性,指向它的构造函数。每一个实例也有一个constructor属性,默认调用prototype对象的constructor属性。

function Person(){ } Car.prototype.say = "def"; function Car(){ } //Car.prototype = { // constructor : Person //} var car = new Car(); console.log(car.constructor);//function Car(){} //console.log(car.constructor);//function Person(){} //注释掉的部分:可以更改对象car的构造函数, // 它本来是function Car(){}实例化的一个对象, // 现在Car.prototype = {constructor : Person}让它指向构造函数function Person(){} 3._proto_,查看原型

new一个构造函数,相当于实例化一个对象,这期间其实进行了这三个步骤:

在构造函数的逻辑最顶端隐式的新建一个this对象,this其实不是不是一个 空对象,
var this = { proto : Person.prototype}//__proto__属性 指向的是对象原型。 (每个对象都有__proto__属性,该属性指向一个对象,就是构造函数Person的原型对象(Person.prototype))

去调用构造函数Person,从而设置对象的属性和方法并初始化。并返回this对象。(把this返回,这样每一个实例化的对象就有__proto__属性了。)

上面步骤完成后,这个对象就与构造函数Person再无联系,这个时候即使构造函数Person再加任何成员,都不再影响已经实例化了的对象了。(此时该对象有
了自己的属性之后,同时具有了构造函数Person的原型对象的所有成员)

a.用法:

Person.prototype.name = 'abc'; function Person(){ //var this = { // __proto__ : Person.prototype//指向的是对象原型 // } } var obj ={ name : "sunny" } var person = new Person(); person.__proto__ = obj; console.log(person.name);

在这里插入图片描述

当new一个对象的时候,开始查找属性name,
先看自己的构造函数里面有没有name属性,如果有就直接用,
如果没有就沿着this里面__proto__ 属性去对象的原型里面查找,这个时候我改变了person.proto = obj;让它指向对象obj,值就是sunny

b.’.'的写法改变原型对象属性的值,那么结果也会跟着改

Person.prototype.name = 'sunny'; function Person(){ //var this = {__proto__ : Person.prototype} } // Person.prototype.name = 'cherry'; var person = new Person(); Person.prototype.name = 'cherry'; console.log(person.name)

无论Person.prototype.name = 'cherry’放在var person = new Person();上面还是下面,输出都是cherry,因为它改变的是__proto__ 指向的Person.prototype上的值。值都变了,person.name必然会变化。

c.改变原型让它指向另外的一个空间

var obj = {name : "a"}; var obj1 = obj;//obj和obj1先指向同一个房间, obj = {name : "b"};//obj它又指向另外一个房间 console.log(obj1.name);//a console.log(obj.name);//b Person.prototype.name = 'sunny'; function Person(){ //var this = {__proto__ : Person.prototype} } var person = new Person(); Person.prototype = { //这是把原型给改了,换了一个新对象 name : 'cherry' } console.log(person.name)//sunny

首先new一个对象person,调用构造函数,里面隐式的var this = {proto : Person.prototype}
让__proto__ 和 Person.prototype指向同一个空间,然后返回this,这个对象就构建完了。
然后Person.prototype = { name : ‘cherry’ }把自己的空间换了,但是 proto 没有换,__proto__它还是指向原来的Person.prototype的空间值就是sunny。

那么查找name属性的时候,去__proto__里面找到Person.prototype.name ,结果就是sunny.。
就像下面的过程:

Person.prototype = {name : "sunny"}; _proto_ = Person.prototype; Person.prototype = {name : "cherry"}; console.log(_proto_.name);//sunny Person.prototype.name = 'sunny'; function Person(){ //var this = {__proto__ : Person.prototype} } Person.prototype = { name : 'cherry' } var person = new Person(); console.log(person.name)//cherry

这个就是Person.prototype = {name : ‘cherry’ }改变了原型对象思维指向,让它的值为cherry,然后再实例化一个对象的时候再调用都早函数的时候就会查找__proto__的Person.prototype为cherry。

d,.改变原型和改变原型属性的值综合起来:

Person.prototype.name = 'sunny'; function Person(){ //var this = {__proto__ : Person.prototype} } Person.prototype = { //改变原型 name : 'cherry' } Person.prototype.name = 'sunny';//改变原型属性的值 var person = new Person(); console.log(person.name)//sunny

在这里插入图片描述

首先是__proto__和 Person.prototype一起指向一个空间值是sunny
然后Person.prototype指向另外一个空间值是cherry,
然后再把第二次Person.prototype指向的空间的内容替换为sunny,最后实例化对象的时候,发现自己没有这个name属性,
根据__proto__ 去原型里面查找,找到的就是第二次指向的空间里里面的值sunny。

1.每当代码读取某个对象的某个属性的时候,都会执行一次搜索。首先从对象实例本身开始,如果在实例中找到了该属性,则返回该属性的值,
2.如果没有找到,则顺着原型链指针向上,到原型对象中去找,如果如果找到就返回该属性值。
3.如果为对象实例添加了一个属性,与原型中属性同名,该属性会屏蔽掉原型中的同名属性
4.使用delete可以删除自己实例中的属性,但是原型中的属性是删除不了的。

原型链

可以把对象串联起来的链式结构

function CreateDog(name,color){ this.name = name; this.color = color; } CreateDog.prototype.say = function(){ console.log(this.color + this.name + '在叫!'); }; var dog = new CreateDog('萨摩耶','白色'); dog.say();

访问原型的方法

1.通过构造函数来访问
console.log(CreateDog.prototype);//Object { say: say(), … }
2. 通过实例化的对象来访问
console.log(dog.proto);//Object { say: say(), … }

js 在创建对象(任何对象,普通对象和函数对象)的时候,都有一个__proto__的属性,
这个属性用于指向创建他的函数对象的原型对象prototype
console.log(dog.proto === CreateDog.prototype);//true

同样的,CreateDog.prototype 对象也有一个__proto__ 指向创建他的函数的原型对象 (object)的prototype

console.log(CreateDog.prototype.proto === Object.prototype);//true

Object.prototype 也有一个__proto__ 指向null

console.log(Object.prototype.proto === null);//true

原型链 特点是:__proto__ 属性

Object.prototype是原型链的终端

字面量创建对象的方法其实和new Object()的方法是一模一样的。

var obj = {}; var obj1 = new Object();

它们是相等的。

obj.__proto__ ---->Object.prototype obj1.__proto__ ---->Object.prototype Person.prototype = {} ------->Object.prototype function Person(){ }

函数的原型就是一个字面量的形式,所以原型链的终端就是Object.prototype

利用Object.create(原型)创建一个对象*

//var obj = Object.create(原型) Person.prototype.name = "sunny"; function Person() { } var person = Object.create(Person.prototype); console.log(person.name);//sunny

在这里插入图片描述
绝大多数对象的最终都会继承于Object.prototype,但是也有不继承的情况

Object.create(原型)里面的"原型"必须是一个Object对象或者空值。(放原始值会报错)

var obj = Object.create(null); var obj1 = Object.create(123);//报错

现在创建一个对象,把null放进去,发现这个对象什么属性都没有了,没原型了。
在这里插入图片描述
调用toString()不行,它自己没有toString方法,它也没原型连__proto__属性都没有,所以根本找不到toString方法,就会报错。

人为加上_proto_属性,系统不认可
在这里插入图片描述

所以说是绝大多数对象继承于Object.prototype,不是所有的对象,因为现在的这个Object.create(null)创建的对象根本没有继承属性,连原型都没有。

toString()方法 undefined和null不能调用tostring方法。 var num = 123; num.toString()

可以调用tostring()方法,因为数字可以通过包装类来一层层访问,包装类肯定是对象,然后对象的原型链的终端是Object,它有tostring方法。

但是undefined和null也不是对象,也没有包装类,他就是原始值,没有原型,不可能可以调用tostring()方法。

数字num调用toString()方法的原理:

var num = 123; //num.toString();------->new Number(num).toString(); //number重写toString Number.prototype.toString = function() {} //Number.prototype.__proto__ = Object.prototype //Object.prototype.toString = function () {}

首先Object.prototype上有一个toString方法,每一个继承Object.prototype的都可以调用,但是他们自己也重写了这个方法,就是Number.prototype.toString = function() {},每次调用toString()就是调用的自己重写的toString()方法,方便打印出自己想要的结果。

document.write()本质上是调用tostirng方法

var obj = Object.create(null); document.write(obj);//会报错 document.write(obj.toString());//会报错,因为它没有原型,更不会有toString()方法 ------------------------------------------------------- //现在人为的加上toString()方法 var obj = Object.create(null); obj.toString = function() { return '访问'; } document.write(obj);//访问 document.write(obj.toString());//访问 //证明document.write()本质上是调用tostirng方法

在这里插入图片描述

call/apply

call() 会改变方法内部this的指向,指向第一个参数,后面的参数是正常传实参。
apply()第一个参数同样是指向的对象,但实参只能传一个数组形式的。
即:

call需要把实参按照行形参的个数传进去。

apply需要把实参放进一个arguments传进去。

两个方法的第一个参数必须是对象本身

/*call()方法*/ function.call(thisObj[, arg1[, arg2[, [,...argN]]]]);

call:调用一个对象的一个方法,用另一个对象替换当前对象。例如:B.call(A, args1,args2);即A对象调用B对象的方法

/*apply()方法*/ function.apply(thisObj[, argArray])

apply:调用一个对象的一个方法,用另一个对象替换当前对象。例如:B.apply(A, arguments);即A对象应用B对象的方法。

从定义中可以看出,call和apply都是调用一个对象的一个方法,用另一个对象替换当前对象。 而不同之处在于传递的参数,apply最多只能有两个参数——新this对象和一个数组argArray,如果arg不是数组则会报错TypeError; apply以数组传递参数,call独立传递。 call则可以传递多个参数,第一个参数和apply一样,是用来替换的对象,后边是参数列表。 基本语法: function myFunction(a, b) { return a * b; } myObject = myFunction.call(myObject, 10, 2); // 返回 20 function myFunction(a, b) { return a * b; } myArray = [10, 2]; myObject = myFunction.apply(myObject, myArray); // 返回 20

在 JavaScript 严格模式(strict mode)下, 在调用函数时第一个参数会成为 this 的值, 即使该参数不是一个对象。

在 JavaScript 非严格模式(non-strict mode)下, 如果第一个参数的值是 null 或 undefined, 它将使用全局对象替代。
在这里插入图片描述


作者:周偏偏偏



Web web前端 web前端开发

需要 登录 后方可回复, 如果你还没有账号请 注册新账号
相关文章