Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
JavaScript 中的所有事物都是對象:字符串、數(shù)值、數(shù)組、函數(shù)...
此外,JavaScript 允許自定義對象。
JavaScript 提供多個內(nèi)建對象,比如 String、Date、Array 等等。
對象只是帶有屬性和方法的特殊數(shù)據(jù)類型。
屬性是與對象相關(guān)的值。
訪問對象屬性的語法是:
_objectName_._propertyName_
這個例子使用了 String 對象的 length 屬性來獲得字符串的長度:
var message="Hello World!";
var x=message.`length`;
在以上代碼執(zhí)行后,x 的值將是:
12
方法是能夠在對象上執(zhí)行的動作。
您可以通過以下語法來調(diào)用方法:
_objectName_._methodName_()
這個例子使用了 String 對象的 toUpperCase() 方法來將文本轉(zhuǎn)換為大寫:
var message="Hello world!";
var x=message.`toUpperCase()`;
在以上代碼執(zhí)行后,x 的值將是:
HELLO WORLD!
通過 JavaScript,您能夠定義并創(chuàng)建自己的對象。
創(chuàng)建新對象有兩種不同的方法:
這個例子創(chuàng)建了對象的一個新實例,并向其添加了四個屬性:
person=new Object();
person.firstname="Bill";
person.lastname="Gates";
person.age=56;
person.eyecolor="blue";
替代語法(使用對象 literals):
person={firstname:"John",lastname:"Doe",age:50,eyecolor:"blue"};
本例使用函數(shù)來構(gòu)造對象:
function person(firstname,lastname,age,eyecolor)
{
this.firstname=firstname;
this.lastname=lastname;
this.age=age;
this.eyecolor=eyecolor;
}
一旦您有了對象構(gòu)造器,就可以創(chuàng)建新的對象實例,就像這樣:
var myFather=new person("Bill","Gates",56,"blue");
var myMother=new person("Steve","Jobs",48,"green");
您可以通過為對象賦值,向已有對象添加新屬性:
假設(shè) personObj 已存在 - 您可以為其添加這些新屬性:firstname、lastname、age 以及 eyecolor:
person.firstname="Bill";
person.lastname="Gates";
person.age=56;
person.eyecolor="blue";
x=person.firstname;
在以上代碼執(zhí)行后,x 的值將是:
Bill
方法只不過是附加在對象上的函數(shù)。
在構(gòu)造器函數(shù)內(nèi)部定義對象的方法:
function person(firstname,lastname,age,eyecolor)
{
this.firstname=firstname;
this.lastname=lastname;
this.age=age;
this.eyecolor=eyecolor;
this.changeName=changeName;
function changeName(name)
{
this.lastname=name;
}
}
changeName() 函數(shù) name 的值賦給 person 的 lastname 屬性。
現(xiàn)在您可以試一下:
myMother.changeName("Ballmer");
JavaScript 是面向?qū)ο蟮恼Z言,但 JavaScript 不使用類。
在 JavaScript 中,不會創(chuàng)建類,也不會通過類來創(chuàng)建對象(就像在其他面向?qū)ο蟮恼Z言中那樣)。
JavaScript 基于 prototype,而不是基于類的。
JavaScript for...in 語句循環(huán)遍歷對象的屬性。
for (對象中的變量)
{
要執(zhí)行的代碼
}
注釋:for...in 循環(huán)中的代碼塊將針對每個屬性執(zhí)行一次。
循環(huán)遍歷對象的屬性:
var person={fname:"Bill",lname:"Gates",age:56};
for (x in person)
{
txt=txt + person[x];
}
我建了一個前端學(xué)習(xí)群,有興趣學(xué)習(xí)的同學(xué)可以關(guān)注我:前端學(xué)習(xí)交流 - 知乎
javascript對象
javascript中的所有事物都是對象:字符串,數(shù)值,數(shù)組,函數(shù).....
此外,javascript允許自定義對象.
javascript提供多個內(nèi)建對象,比如:String,Date,Array等等.對象只是帶有屬性和方法的特殊數(shù)據(jù)類型.
●布爾型可以是一個對象.
●數(shù)字型可以是一個對象.
●字符串可以是一個對象.
●日期是一個對象.
●正則表達式也是對象.
●數(shù)組是一個對象.
●甚至函數(shù)也可以是對象
var person={name:"John",age:31, favColor:"green",height:178};
屬性:name,屬性值:John. 以此類推.
javascript對象屬性
可以通過兩種方式訪問對象屬性.
objectName.propertyName
或者objectName['propertyName']
javascript的內(nèi)置長度屬性用于計算屬性或字符串中的字符數(shù).
var course={name:"JS",lesson:34};
document.write(course.name.length)
// ->2
對象方法
對象方法是一個包含函數(shù)定義的屬性.
調(diào)用方法:
objectName.methodName()
使用String對象的toUpperCase()方法來將文本轉(zhuǎn)換為大寫:
var message="Coffee is lonely without cups";
var x=message.toUpperCase();
這樣Coffee is lonely without cups 就會變成COFFEE IS LONELY WITHOUT CUPS
方法是作為對象屬性存儲的函數(shù).
二 對象構(gòu)造器
使用函數(shù)來構(gòu)建對象:
function person(firstname,lastname,age,eyecolor){
this.firstname=firstname;
this.lastname=lastname;
this.age=age;
this.eyecolor=eyecolor; }
在javascript中,this通常指向我們正在執(zhí)行的函數(shù)本身,或者指向該函數(shù)所屬的對象(運行時)
創(chuàng)建javascript對象實例
一旦你有了對象構(gòu)造器,就可以創(chuàng)建新的對象實例.
var myFather= new person("John","Doe",50,"blue");
var myMother=new person("Sally","Rally",48,"green") ;
document.write(myFather.age); //->50
document.write(myMother.name);//->Sally
myFather和myMother是person對象的實例.
三 對象初始化
使用對象literal或initializer語法創(chuàng)建單個對象.(literal:字面量.initializer:初始化程序,初始化器)
var John={name:"John",age:25};
var Loen={name:"Loen",age:28};
使用對象初始化器
空格和換行符并不重要.對象定義可以跨多行.
var John={
name:"John",
age:25};
var Loen={
name:"Loen",
age:28};
無論如何創(chuàng)建對象,訪問屬性和方法的語法不會改變.
document.write(Loen.age);
四 添加方法
方法是存儲在對象屬性中的函數(shù).
創(chuàng)建對象函數(shù):
function methodName(){code lines }
訪問對象函數(shù):
objectName.methodName()
this關(guān)鍵字是當(dāng)前對象的引用,這意味著你可以使用this來訪問對象屬性和方法.
在構(gòu)造函數(shù)中定義方法:
function person(name,age){
this.name=name;
this.age=age;
this.changeName=function (name){
this.name=name;}
}
var p=new person("Loen",28);
p.changeName("John");
在上面的例子中,我們定義了一個名為changeName的方法,該方法是一個函數(shù),它接受參數(shù)名稱并將其分配給對象的name屬性.
添加方法:
可以在構(gòu)造函數(shù)的外部定義一個函數(shù),通過函數(shù)名關(guān)聯(lián)到對象的屬性上;
function person(name,age){
this.name=name;
this.age=age;
this.yearOfBirth=bornYear; //關(guān)聯(lián)bornYear函數(shù)
}
function bornYear(){
return new Date().getFullYear()-this.age(); }
以上的代碼中new Date().getFullYear()是獲取今年完整的年份時間.如:2018.
我們已將對象的yearOfBirth屬性賦予bornYear函數(shù).當(dāng)通過對象調(diào)用bornYear函數(shù)時,bornYear函數(shù)中的this指向這個對象.
將函數(shù)關(guān)聯(lián)到對象屬性時不需要寫函數(shù)后邊的括號.
調(diào)用方法:
function person(name,age){
this.name=name;
this.age=age;
this.yearOfBirth=bornYear;
}
function bornYear(){
return new Date().getFullYear()-this.age; }
var p=new person("Loen",27);
document.write(p.yearOfBirth());
// ->1991
通過對象的屬性名調(diào)用函數(shù)而不是直接調(diào)用函數(shù).
每天堅持進步一點點!
在seata整合nacos完成分布式的部署中,我們學(xué)習(xí)了Seata的集群部署,在這篇文章中,我們使用SpringBoot整合Seata實現(xiàn)分布式事務(wù)功能,此處使用的是Seata的AT模式。
我們存在2個服務(wù) 賬戶服務(wù) account-service 和 訂單服務(wù) order-service,在訂單服務(wù)中調(diào)用 賬戶服務(wù)。
訂單服務(wù)中調(diào)用賬戶服務(wù)是通過 RestTemplate來實現(xiàn)的。
測試場景:
1、賬戶服務(wù)正常,訂單服務(wù)正常,結(jié)果:賬戶服務(wù)正常扣款,產(chǎn)生訂單。
2、賬戶服務(wù)正常,訂單服務(wù)正常,在整個分布式事務(wù)中發(fā)生了異常,結(jié)果: 賬戶服務(wù)沒有扣款,沒有產(chǎn)生訂單。
SpringBoot、Seata、Mybatis、nacos、druid
SpringBoot、Seata、Mybatis、nacos、Hikari
其中 SpringBoot 整合 Seata 是通過 seata-spring-boot-starter 這個來實現(xiàn)的,不使用 seata-all來實現(xiàn)。
賬戶服務(wù),提供一個簡單的扣除賬戶余額的功能,比較簡單。
注意項:
1、開啟自動數(shù)據(jù)源代理。
2、引入druid,不需要自動配置數(shù)據(jù)源。
3、注意事務(wù)分組
此處只引入幾個 核心的 包,其余的包沒有列在下方,比如mybatis等,注意和seata整合使用的是seata-spring-boot-starter
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>1.4.2</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.4</version>
</dependency>
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
<version>1.3.2</version>
</dependency>
create database seata_account;
use seata_account;
create table account(
id int unsigned auto_increment primary key comment '主鍵',
name varchar(20) comment '用戶名',
balance bigint comment '賬戶余額,單位分'
) engine=InnoDB comment '賬戶表';
insert into account(id,name,balance) values (1,'張三',100000);
CREATE TABLE IF NOT EXISTS `undo_log`
(
`branch_id` BIGINT NOT NULL COMMENT 'branch transaction id',
`xid` VARCHAR(128) NOT NULL COMMENT 'global transaction id',
`context` VARCHAR(128) NOT NULL COMMENT 'undo_log context,such as serialization',
`rollback_info` LONGBLOB NOT NULL COMMENT 'rollback info',
`log_status` INT(11) NOT NULL COMMENT '0:normal status,1:defense status',
`log_created` DATETIME(6) NOT NULL COMMENT 'create datetime',
`log_modified` DATETIME(6) NOT NULL COMMENT 'modify datetime',
UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)
) ENGINE = InnoDB COMMENT ='AT transaction mode undo table';
每個業(yè)務(wù)庫必須存在一張 undo_log表
提供一個接口,實現(xiàn)產(chǎn)生訂單,扣除賬戶余額。
注意事項:
1、訂單服務(wù) 關(guān)閉默認的數(shù)據(jù)源代理,自己配置數(shù)據(jù)源代理。
2、使用 Hikari數(shù)據(jù)源來實現(xiàn),因為使用的是 AT模式,所以需要使用 DataSourceProxy 來代理數(shù)據(jù)源。
3、訂單服務(wù)調(diào)用賬戶服務(wù)是采用的 RestTemplate,因此需要手動配置RestTemplate的攔截器,實現(xiàn)xid的傳輸。
4、在seata1.4.2中存在一個bug,如果業(yè)務(wù)表中數(shù)據(jù)類型是datetime類型,可能undolog無法序列化成功,可以采用timestamp或別的方式來處理。
5、業(yè)務(wù)庫中需要存在 undo_log 表。
此處不引入 druid,注意和seata整合使用的是seata-spring-boot-starter
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>1.4.2</version>
</dependency>
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
<version>1.3.2</version>
</dependency>
package com.huan.seata.config;
import com.zaxxer.hikari.HikariDataSource;
import io.seata.rm.datasource.DataSourceProxy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
@Configuration
public class DataSourceConfig {
@Autowired
private DataSourceProperties dataSourceProperties;
@Bean
public DataSource dataSourceProxy() {
HikariDataSource hikariDataSource = new HikariDataSource();
hikariDataSource.setJdbcUrl(dataSourceProperties.getUrl());
hikariDataSource.setUsername(dataSourceProperties.getUsername());
hikariDataSource.setPassword(dataSourceProperties.getPassword());
hikariDataSource.setDriverClassName(dataSourceProperties.getDriverClassName());
return new DataSourceProxy(hikariDataSource);
}
}
在 AT模式種,數(shù)據(jù)源代理一定要是 DataSourceProxy這個。
@Service
@RequiredArgsConstructor
@Slf4j
public class BusinessServiceImpl implements BusinessService {
private final OrderService orderService;
private final RestTemplate restTemplate;
@Override
@GlobalTransactional(rollbackFor = Exception.class)
public void createAccountOrder(Integer accountId, Long amount, boolean hasException) {
System.out.println("createAccountOrder:" + RootContext.getXID());
// 1、遠程扣減賬戶余額
remoteDebit(accountId, amount);
// 2、下訂單
orderService.createOrder(accountId, amount);
if (hasException) {
throw new RuntimeException("發(fā)生了異常,分布式事物需要會滾");
}
}
private void remoteDebit(Integer accountId, Long amount) {
String url = "http://localhost:50001/account/debit?id=" + accountId + "&amount=" + amount;
String result = restTemplate.getForObject(url, String.class);
log.info("遠程扣減庫存結(jié)果:[{}]", result);
}
}
此處以訂單服務(wù)來演示,如何和配置中心對應(yīng)上的。
每個服務(wù)的事務(wù)分組可能不一樣,但是需要和配置中心對應(yīng)上。
比如:
order-service 中的配置分組為:seata.tx-service-group=tx_order_service_group
配置中心必須存在 service.vgroupMapping.tx_order_service_group=default 配置項,default是集群,是服務(wù)端配置文件中指定的
訪問:http://localhost:50002/createOrder?accountId=1&amount=10&hasException=false
正常創(chuàng)建訂單,和扣除余額。
訪問: http://localhost:50002/createOrder?accountId=1&amount=10&hasException=true
不產(chǎn)生訂單,不扣除余額。
A: 異常:io.seata.common.exception.FrameworkException: can not register RM,err:can not connect to services-server.
@Transactional 可與 DataSourceTransactionManager 和 JTATransactionManager 連用分別表示本地事務(wù)和XA分布式事務(wù),大家常用的是與本地事務(wù)結(jié)合。當(dāng)與本地事務(wù)結(jié)合時,@Transactional和@GlobalTransaction連用,@Transactional 只能位于標(biāo)注在@GlobalTransaction的同一方法層次或者位于@GlobalTransaction 標(biāo)注方法的內(nèi)層。這里分布式事務(wù)的概念要大于本地事務(wù),若將 @Transactional 標(biāo)注在外層會導(dǎo)致分布式事務(wù)空提交,當(dāng)@Transactional 對應(yīng)的 connection 提交時會報全局事務(wù)正在提交或者全局事務(wù)的xid不存在。
由于業(yè)務(wù)提交,seata記錄當(dāng)前鏡像后,數(shù)據(jù)庫又進行了一次時間戳的更新,導(dǎo)致鏡像校驗不通過。
解決方案1: 關(guān)閉數(shù)據(jù)庫的時間戳自動更新。數(shù)據(jù)的時間戳更新,如修改、創(chuàng)建時間由代碼層面去維護,比如MybatisPlus就能做自動填充。
解決方案2: update語句別把沒更新的字段也放入更新語句。
Seata 注冊中心不能注冊 0.0.0.0 或 127.0.0.1 的地址,當(dāng)自動注冊為上述地址時可以通過啟動參數(shù) -h 或容器環(huán)境變量SEATA_IP來指定。當(dāng)和業(yè)務(wù)服務(wù)處于不同的網(wǎng)絡(luò)時注冊地址可以指定為 NAT_IP或公網(wǎng)IP,但需要保證注冊中心的健康檢查探活是通暢的。
以上的幾個問題,來自seata官網(wǎng) : http://seata.io/zh-cn/docs/overview/faq.html
代碼地址:https://gitee.com/huan1993/spring-cloud-parent/tree/master/seata/seata-springboot-mybatis
*請認真填寫需求信息,我們會在24小時內(nèi)與您取得聯(lián)系。