首页技术hasownproperty()(hasownproperty的用法)

hasownproperty()(hasownproperty的用法)

编程之家2026-05-27845次浏览

各位老铁们,大家好,今天由我来为大家分享hasownproperty(),以及hasownproperty的用法的相关问题知识,希望对大家有所帮助。如果可以帮助到大家,还望关注收藏下本站,您的支持是我们最大的动力,谢谢大家了哈,下面我们开始吧!

hasownproperty()(hasownproperty的用法)

hasOwnProperty报错怎么解决

解决 hasOwnProperty报错的关键在于确保操作对象是真正的对象类型。以下是具体分析和解决方案:

1.变量类型错误原因:变量被赋值为非对象类型(如字符串、数字、null、undefined等)。解决方法:使用 typeof或 Object.prototype.toString.call()检查类型。

示例代码:if(typeof myVariable==='object'&& myVariable!== null&& myVariable.hasOwnProperty('property')){//安全操作}

注意:typeof null返回"object",需额外排除 null。

2.未定义的变量原因:变量未声明或拼写错误,或作用域问题导致变量不可见。解决方法:检查变量名拼写,确保在调用前已正确定义。

使用代码编辑器的智能提示或 console.log(variable)调试。

hasownproperty()(hasownproperty的用法)

3.异步操作问题原因:在对象初始化完成前调用 hasOwnProperty(如异步数据未返回)。解决方法:使用 Promise、async/await确保对象就绪后再操作。

示例:async function checkProperty(){ const data= await fetchData();//假设是异步获取对象 if(data&& typeof data==='object'&& data.hasOwnProperty('key')){//操作属性}}

4.原型链污染原因:手动修改了 Object.prototype,导致 hasOwnProperty被覆盖或失效。解决方法:避免直接修改原型链。

如需检查属性,改用 Object.prototype.hasOwnProperty.call(obj,'key')显式调用。

通用排查步骤检查变量类型:确认操作对象是 object且非 null。调试变量值:通过 console.log输出变量,确认其内容。代码审查:检查变量赋值逻辑,尤其是循环或条件分支中的赋值。防御性编程:始终在调用前进行类型检查。示例:安全使用 hasOwnPropertyfunction safeHasProperty(obj, key){ return obj&& typeof obj==='object'&& Object.prototype.hasOwnProperty.call(obj, key);}//使用const myObj={ name:"test"};if(safeHasProperty(myObj,'name')){ console.log("属性存在");}总结:hasOwnProperty报错通常由非对象调用引起,通过类型检查、调试变量和规范异步操作即可解决。建议封装安全检查函数,避免重复代码。

javascript垃圾回收方法

一、垃圾回收的必要性

hasownproperty()(hasownproperty的用法)

下面这段话引自《JavaScript权威指南(第四版)》

由于字符串、对象和数组没有固定大小,所有当他们的大小已知时,才能对他们进行动态的存储分配。JavaScript程序每次创建字符串、数组或对象时,解释器都必须分配内存来存储那个实体。只要像这样动态地分配了内存,最终都要释放这些内存以便他们能够被再用,否则,JavaScript的解释器将会消耗完系统中所有可用的内存,造成系统崩溃。

这段话解释了为什么需要系统需要垃圾回收,JS不像C/C++,他有自己的一套垃圾回收机制(Garbage Collection)。JavaScript的解释器可以检测到何时程序不再使用一个对象了,当他确定了一个对象是无用的时候,他就知道不再需要这个对象,可以把它所占用的内存释放掉了。例如:

var a="before";

var b="override a";

var a= b;//重写a

这段代码运行之后,“before”这个字符串失去了引用(之前是被a引用),系统检测到这个事实之后,就会释放该字符串的存储空间以便这些空间可以被再利用。

二、垃圾回收原理浅析

现在各大浏览器通常用采用的垃圾回收有两种方法:标记清除、引用计数。

1、标记清除

这是javascript中最常用的垃圾回收方式。当变量进入执行环境是,就标记这个变量为“进入环境”。从逻辑上讲,永远不能释放进入环境的变量所占用的内存,因为只要执行流进入相应的环境,就可能会用到他们。当变量离开环境时,则将其标记为“离开环境”。

垃圾收集器在运行的时候会给存储在内存中的所有变量都加上标记。然后,它会去掉环境中的变量以及被环境中的变量引用的标记。而在此之后再被加上标记的变量将被视为准备删除的变量,原因是环境中的变量已经无法访问到这些变量了。最后。垃圾收集器完成内存清除工作,销毁那些带标记的值,并回收他们所占用的内存空间。

关于这一块,建议读读Tom大叔的几篇文章,关于作用域链的一些知识详解,读完差不多就知道了,哪些变量会被做标记。

2、引用计数

另一种不太常见的垃圾回收策略是引用计数。引用计数的含义是跟踪记录每个值被引用的次数。当声明了一个变量并将一个引用类型赋值给该变量时,则这个值的引用次数就是1。相反,如果包含对这个值引用的变量又取得了另外一个值,则这个值的引用次数就减1。当这个引用次数变成0时,则说明没有办法再访问这个值了,因而就可以将其所占的内存空间给收回来。这样,垃圾收集器下次再运行时,它就会释放那些引用次数为0的值所占的内存。

但是用这种方法存在着一个问题,下面来看看代码:

function problem(){

var objA= new Object();

var objB= new Object();

objA.someOtherObject= objB;

objB.anotherObject= objA;

}

在这个例子中,objA和objB通过各自的属性相互引用;也就是说这两个对象的引用次数都是2。在采用引用计数的策略中,由于函数执行之后,这两个对象都离开了作用域,函数执行完成之后,objA和objB还将会继续存在,因为他们的引用次数永远不会是0。这样的相互引用如果说很大量的存在就会导致大量的内存泄露。

我们知道,IE中有一部分对象并不是原生JavaScript对象。例如,其BOM和DOM中的对象就是使用C++以COM(Component Object

Model,组件对象)对象的形式实现的,而COM对象的垃圾回收器就是采用的引用计数的策略。因此,即使IE的Javascript引擎使用标记清除的策略来实现的,但JavaScript访问的COM对象依然是基于引用计数的策略的。说白了,只要IE中涉及COM对象,就会存在循环引用的问题。看看下面的这个简单的例子:

var element= document.getElementById("some_element");

var myObj=new Object();

myObj.element= element;

element.someObject= myObj;

上面这个例子中,在一个DOM元素(element)与一个原生JavaScript对象(myObj)之间建立了循环引用。其中,变量myObj有一个名为element的属性指向element;而变量element有一个名为someObject的属性回指到myObj。由于循环引用,即使将例子中的DOM从页面中移除,内存也永远不会回收。

不过上面的问题也不是不能解决,我们可以手动切断他们的循环引用。

myObj.element= null;

element.someObject=null;

这样写代码的话就可以解决循环引用的问题了,也就防止了内存泄露的问题。

三、减少JavaScript中的垃圾回收

首先,最明显的,new关键字就意味着一次内存分配,例如 new Foo()。最好的处理方法是:在初始化的时候新建对象,然后在后续过程中尽量多的重用这些创建好的对象。

另外还有以下三种内存分配表达式(可能不像new关键字那么明显了):

{}(创建一个新对象)

[](创建一个新数组)

function(){…}(创建一个新的方法,注意:新建方法也会导致垃圾收集!!)

1、对象object优化

为了最大限度的实现对象的重用,应该像避使用new语句一样避免使用{}来新建对象。

{“foo”:”bar”}这种方式新建的带属性的对象,常常作为方法的返回值来使用,可是这将会导致过多的内存创建,因此最好的解决办法是:每一次函数调用完成之后,将需要返回的数据放入一个全局的对象中,并返回此全局对象。如果使用这种方式,就意味着每一次方法调用都会导致全局对象内容的修改,这有可能会导致错误的发生。因此,一定要对此全局对象的使用进行详细的注释和说明。

有一种方式能够保证对象(确保对象prototype上没有属性)的重复利用,那就是遍历此对象的所有属性,并逐个删除,最终将对象清理为一个空对象。

cr.wipe(obj)方法就是为此功能而生,代码如下:

//删除obj对象的所有属性,高效的将obj转化为一个崭新的对象!

cr.wipe= function(obj){

for(var p in obj){

if(obj.hasOwnProperty(p))

delete obj[p];

}

};

有些时候,你可以使用cr.wipe(obj)方法清理对象,再为obj添加新的属性,就可以达到重复利用对象的目的。虽然通过清空一个对象来获取“新对象”的做法,比简单的通过{}来创建对象要耗时一些,但是在实时性要求很高的代码中,这一点短暂的时间消耗,将会有效的减少垃圾堆积,并且最终避免垃圾回收暂停,这是非常值得的!

2、数组array优化

将[]赋值给一个数组对象,是清空数组的捷径(例如: arr= [];),但是需要注意的是,这种方式又创建了一个新的空对象,并且将原来的数组对象变成了一小片内存垃圾!实际上,将数组长度赋值为0(arr.length= 0)也能达到清空数组的目的,并且同时能实现数组重用,减少内存垃圾的产生。

3、方法function优化

方法一般都是在初始化的时候创建,并且此后很少在运行时进行动态内存分配,这就使得导致内存垃圾产生的方法,找起来就不是那么容易了。但是从另一角度来说,这更便于我们寻找了,因为只要是动态创建方法的地方,就有可能产生内存垃圾。例如:将方法作为返回值,就是一个动态创建方法的实例。

在游戏的主循环中,setTimeout或requestAnimationFrame来调用一个成员方法是很常见的,例如:

setTimeout(

(function(self){

return function(){

self.tick();

};

})(this), 16)

每过16毫秒调用一次this.tick(),嗯,乍一看似乎没什么问题,但是仔细一琢磨,每一次调用都返回了一个新的方法对象,这就导致了大量的方法对象垃圾!

为了解决这个问题,可以将作为返回值的方法保存起来,例如:

// at startup

this.tickFunc=(

function(self){

return function(){

self.tick();

};

}

)(this);

// in the tick() function

setTimeout(this.tickFunc, 16);

相比于每次都新建一个方法对象,这种方式在每一帧当中重用了相同的方法对象。这种方式的优势是显而易见的,而这种思想也可以应用在任何以方法为返回值或者在运行时创建方法的情况当中。

4、高级技术

从根本上来说,javascript本身就是围绕着垃圾收集来设计的。随着我们工作的进行,避免内存垃圾变得越来越困难。因为很多方便实用的Javascript库方法也会产生一些新的对象。对于这些库方法产生的垃圾,我们束手无策,只能重新翻看文档,并且检查方法的返回值。例如,数组的slice方法返回一个新的数组(在不修改原数组的基础上,截取出一部分作为新数组),字符串的substr方法返回一个新的字符串(在不修改原字符串的基础上,截取出一部分字符串作为返回值)等等。

调用这些库方法,将会创建内存垃圾,而你能做的,只有避免调用这些方法,或者用不创建系统垃圾的方式重写这些方法(有点极端啦~)。

例如,在Construct 2引擎中,从数组中利用下标来删除一个元素,是经常进行的操作。最初我们是用下面这种方式来实现的:

var sliced= arr.slice(index+ 1);

arr.length= index;

arr.push.apply(arr, sliced);

然而,slice方法会返回一个新的数组对象(数组中的元素是原数组中删掉的部分),并且会通过arr.push.apply方法将元素重新复制回原数组,但是在此操作之后,该数组就成为了一片内存垃圾。由于这是我们引擎中的垃圾产生的热点代码(使用频率非常很高),因此我们利用了迭代的方式重写了上述代码:

for(var i= index, len= arr.length– 1; i< len; i++)

arr[i]= arr[i+ 1];

arr.length= len;

显然,重写大量的库函数是非常痛苦的,因此你必须仔细权衡方法的易用性和内存垃圾产生情况。如果产生大量内存垃圾的方法在动画的每一帧中被多次调用,你可能就会兴高采烈的重写库函数啦。

在递归函数中,通过{}构造空对象,并在递归过程中传递数据,虽然是很方便的。但是更好的方式是:利用一个单独的数组对象作为堆栈,在递归过程中对数组进行push和pop操作。更进一步,不要调用array的pop方法(pop将会使得array的最后一个元素将会变成内存垃圾),而应该使用一个索引来记录数组的最后一个元素的位置,在pop时简单的将索引减一即可;类似的,将索引加1来代替array的push操作,只有当索引对应的元素不存在时,才执行真正的push为数组加入一个新元素。

另外,在任何时候,都应该避免使用向量对象(例如:包含x和y属性的vector2对象)。有些方法将向量对象作为方法返回值,既可以支持返回值的再次修改,又能够将需要的属性一次性返回,使用起来非常方便。但是有时候在一帧动画中,创建了成百上千个这样的向量对象,从而导致严重的垃圾回收性能问题,也是非常常见的。因此最好将这些方法分离成具有独立职责的功能个体,例如:利用getX()和getY()方法(返回具体数据)代替getPosition()方法(返回一个vector2对象)。

四、总结

在Javascript中,彻底避免垃圾回收是非常困难的。垃圾回收机制与实时软件(例如:游戏)的实时性要求,从根本上就是对立的。

但是,为了减少内存垃圾,我们还是可以对javascript代码进行彻底检查,有些代码中存在明显的产生过多内存垃圾的问题代码,这些正是我们需要检查并且完善的。

我认为,只要我们投入更多的精力和关注,实现实时的、低垃圾收集的javascript应用还是很有可能的。毕竟,对于可交互性要求较高的游戏或应用来说,实时性和低垃圾收集,两者都是至关重要。

怎么连接nodejs服务器

前面已经学习了WebSocket API,包括事件、方法和属性。详情:WebSocket(二)--APIWebSocket是基于事件驱动,支持全双工通信。下面通过三个简单例子体验一下。

简单开始

1.安装node。/

2.安装ws模块

ws:是nodejs的一个WebSocket库,可以用来创建服务。

3.server.js

在项目里面新建一个server.js,创建服务,指定8181端口,将收到的消息log出来。

var WebSocketServer= require('ws').Server,

wss= new WebSocketServer({ port: 8181});

wss.on('connection', function(ws){

console.log('client connected');

ws.on('message', function(message){

console.log(message);

});

});

4.建立一个client.html。

在页面上建立一个WebSocket的连接。用send方法发送消息。

var ws= new WebSocket("ws://localhost:8181");

ws.onopen= function(e){

console.log('Connection to server opened');

} function sendMessage(){

ws.send($('#message').val());

}

页面:

View Code

运行之后如下,服务端即时获得客户端的消息。

模拟股票

上面的例子很简单,只是为了演示如何运用nodejs的ws创建一个WebSocket服务器。且可以接受客户端的消息。那么下面这个例子演示股票的实时更新。客服端只需要连接一次,服务器端会不断地发送新数据,客户端收数据后更新UI.页面如下,有五只股票,开始和停止按钮测试连接和关闭。

服务端:

1.模拟五只股票的涨跌。

var stocks={"AAPL": 95.0,"MSFT": 50.0,"AMZN": 300.0,"GOOG": 550.0,"YHOO": 35.0}function randomInterval(min, max){ return Math.floor(Math.random()*(max- min+ 1)+ min);

}var stockUpdater;var randomStockUpdater= function(){ for(var symbol in stocks){ if(stocks.hasOwnProperty(symbol)){ var randomizedChange= randomInterval(-150, 150); var floatChange= randomizedChange/ 100;

stocks[symbol]+= floatChange;

}

} var randomMSTime= randomInterval(500, 2500);

stockUpdater= setTimeout(function(){

randomStockUpdater();

}, randomMSTime);

}

randomStockUpdater();

2.连接建立之后就开始更新数据

wss.on('connection', function(ws){ var sendStockUpdates= function(ws){ if(ws.readyState== 1){ var stocksObj={}; for(var i= 0; i< clientStocks.length; i++){ var symbol= clientStocks[i];

stocksObj[symbol]= stocks[symbol];

} if(stocksObj.length!== 0){ ws.send(JSON.stringify(stocksObj));//需要将对象转成字符串。WebSocket只支持文本和二进制数据

console.log("更新", JSON.stringify(stocksObj));

}

}

} var clientStockUpdater= setInterval(function(){

sendStockUpdates(ws);

}, 1000);

ws.on('message', function(message){ var stockRequest= JSON.parse(message);//根据请求过来的数据来更新。

console.log("收到消息", stockRequest);

clientStocks= stockRequest['stocks'];

sendStockUpdates(ws);

});

客户端:

建立连接:

var ws= new WebSocket("ws://localhost:8181");

onopen直接只有在连接成功后才会触发,在这个时候将客户端需要请求的股票发送给服务端。

var isClose= false; var stocks={"AAPL": 0,"MSFT": 0,"AMZN": 0,"GOOG": 0,"YHOO": 0

}; function updataUI(){

ws.onopen= function(e){

console.log('Connection to server opened');

isClose= false; ws.send(JSON.stringify(stock_request));

console.log("sened a mesg");

}//更新UI

var changeStockEntry= function(symbol, originalValue, newValue){ var valElem=$('#'+ symbol+' span');

valElem.html(newValue.toFixed(2)); if(newValue< originalValue){

valElem.addClass('label-danger');

valElem.removeClass('label-success');

} else if(newValue> originalValue){

valElem.addClass('label-success');

valElem.removeClass('label-danger');

}

}//处理受到的消息

ws.onmessage= function(e){ var stocksData= JSON.parse(e.data);

console.log(stocksData); for(var symbol in stocksData){ if(stocksData.hasOwnProperty(symbol)){

changeStockEntry(symbol, stocks[symbol], stocksData[symbol]);

stocks[symbol]= stocksData[symbol];

}

}

};

}

updataUI();

运行效果如下:只需要请求一次,数据就会不断的更新,效果是不是很赞,不用轮询,也不用长连接那么麻烦了。文章末尾会附上所有源码。

(美股的涨跌和A股的颜色是反的,即红跌绿涨)

实时聊天

上面的例子是连接建立之后,服务端不断给客户端发送数据。接下来例子是一个简单的聊天室类的例子。可以建立多个连接。

1.安装node-uuid模块,用来给每个连接一个唯一号。

2.服务端消息发送

消息类型分notification和message两种,前者是提示信息,后者是聊天内容。消息还包含一个id、昵称和消息内容。在上一节有学习到readyState有四个值,OPEN表示连接建立可以发送消息。如果页面关闭了,为WebSocket.CLOSE。

function wsSend(type, client_uuid, nickname, message){ for(var i= 0; i< clients.length; i++){ var clientSocket= clients[i].ws; if(clientSocket.readyState=== WebSocket.OPEN){

clientSocket.send(JSON.stringify({"type": type,"id": client_uuid,"nickname": nickname,"message": message

}));

}

}

}

3.服务端处理连接

每新增加一个连接,都会发送一条匿名用户的加入的提示消息,如果消息中带有“/nick”认为这一个修改昵称的消息。然后更新客户端的昵称。其他都会当做聊天消息处理。

wss.on('connection', function(ws){ var client_uuid= uuid.v4(); var nickname="AnonymousUser"+ clientIndex;

clientIndex+= 1;

clients.push({"id": client_uuid,"ws": ws,"nickname": nickname});

console.log('client [%s] connected', client_uuid); var connect_message= nickname+" has connected"; wsSend("notification", client_uuid, nickname, connect_message);

console.log('client [%s] connected', client_uuid);

ws.on('message', function(message){ if(message.indexOf('/nick')=== 0){ var nickname_array= message.split(''); if(nickname_array.length>= 2){ var old_nickname= nickname;

nickname= nickname_array[1]; var nickname_message="Client"+ old_nickname+" changed to"+ nickname; wsSend("nick_update", client_uuid, nickname, nickname_message);

}

} else{

wsSend("message", client_uuid, nickname, message);

}

});

处理连接关闭:

var closeSocket= function(customMessage){ for(var i= 0; i< clients.length; i++){ if(clients[i].id== client_uuid){ var disconnect_message; if(customMessage){

disconnect_message= customMessage;

} else{

disconnect_message= nickname+" has disconnected";

} wsSend("notification", client_uuid, nickname, disconnect_message);

clients.splice(i, 1);

}

}

};

ws.on('close', function(){

closeSocket();

});

4.客户端

没有启动时,页面如下,change按钮用来修改昵称。

View Code

js:

//建立连接

var ws= new WebSocket("ws://localhost:8181"); var nickname="";

ws.onopen= function(e){

console.log('Connection to server opened');

}//显示

function appendLog(type, nickname, message){ if(typeof message=="undefined") return; var messages= document.getElementById('messages'); var messageElem= document.createElement("li"); var preface_label; if(type==='notification'){

preface_label="<span class=\"label label-info\">*</span>";

} else if(type=='nick_update'){

preface_label="<span class=\"label label-warning\">*</span>";

} else{

preface_label="<span class=\"label label-success\">"

+ nickname+"</span>";

} var message_text="<h2>"+ preface_label+"&nbsp;&nbsp;"

+ message+"</h2>";

messageElem.innerHTML= message_text;

messages.appendChild(messageElem);

}//收到消息处理

ws.onmessage= function(e){ var data= JSON.parse(e.data);

nickname= data.nickname;

appendLog(data.type, data.nickname, data.message);

console.log("ID: [%s]=%s", data.id, data.message);

}

ws.onclose= function(e){

appendLog("Connection closed");

console.log("Connection closed");

}//发送消息

function sendMessage(){ var messageField= document.getElementById('message'); if(ws.readyState=== WebSocket.OPEN){

ws.send(messageField.value);

}

messageField.value='';

messageField.focus();

}//修改名称

function changName(){ var name=$("#name").val(); if(ws.readyState=== WebSocket.OPEN){

ws.send("/nick"+ name);

}

}

运行结果:

页面关闭之后,连接马上断开。

这种实时响应的体验简直不能太爽,代码也清爽了,前端体验也更好,客户端不用一直发请求,服务端不用等着被轮询。

小结:上面例子的代码都很好理解,接下来学习WebSocket协议。

OK,关于hasownproperty()和hasownproperty的用法的内容到此结束了,希望对大家有所帮助。

html5官方网站?html5游戏网站php网站源码网站(网站代码生成器)